diff --git a/package-lock.json b/package-lock.json index 3855f6e..97c4342 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "lodash": "^4.17.21", "match-sorter": "^6.3.0", "mathjs": "^9.3.0", - "mtgatool-db": "^2.1.10", + "mtgatool-db": "^2.1.11", "mtgatool-shared": "^2.2.0", "qrcode": "^1.5.1", "queue": "^6.0.2", @@ -18209,9 +18209,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mtgatool-db": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/mtgatool-db/-/mtgatool-db-2.1.10.tgz", - "integrity": "sha512-ikUORgLfy/75fSq8D/fkaP7i+oGKhrvhoXD9oP4CsL45vrxbf4aF5rXcoaxUNt743BrCf8NTzFit08q0Ix96jw==", + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/mtgatool-db/-/mtgatool-db-2.1.11.tgz", + "integrity": "sha512-7st1ub5vsTxg7OTg6GhPJijTonPRfbRp2Q75u04h+bxivxtDaUdr16p+pSyChr8yeN0mVzoYxuJ2/Lv5WmJW+w==", "dependencies": { "@peculiar/webcrypto": "^1.1.7", "automerge": "^1.0.1-preview.4", @@ -42488,9 +42488,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mtgatool-db": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/mtgatool-db/-/mtgatool-db-2.1.10.tgz", - "integrity": "sha512-ikUORgLfy/75fSq8D/fkaP7i+oGKhrvhoXD9oP4CsL45vrxbf4aF5rXcoaxUNt743BrCf8NTzFit08q0Ix96jw==", + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/mtgatool-db/-/mtgatool-db-2.1.11.tgz", + "integrity": "sha512-7st1ub5vsTxg7OTg6GhPJijTonPRfbRp2Q75u04h+bxivxtDaUdr16p+pSyChr8yeN0mVzoYxuJ2/Lv5WmJW+w==", "requires": { "@peculiar/webcrypto": "^1.1.7", "automerge": "^1.0.1-preview.4", diff --git a/package.json b/package.json index 4ceb18b..3a6f299 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "lodash": "^4.17.21", "match-sorter": "^6.3.0", "mathjs": "^9.3.0", - "mtgatool-db": "^2.1.10", + "mtgatool-db": "^2.1.11", "mtgatool-shared": "^2.2.0", "qrcode": "^1.5.1", "queue": "^6.0.2", @@ -201,4 +201,4 @@ "type": "git", "url": "https://github.com/mtgatool/mtgatool-desktop.git" } -} +} \ No newline at end of file diff --git a/public/tooldb-worker/index.js b/public/tooldb-worker/index.js index a96c4fb..d2bfa27 100644 --- a/public/tooldb-worker/index.js +++ b/public/tooldb-worker/index.js @@ -1,375 +1,455 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.tooldbworker = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { - let savedPeers = constants_1.DEFAULT_PEERS; - if (err) { - console.error("Error getting saved peers from cache:", err); - } - else if (data) { - try { - const newPeers = JSON.parse(data); - savedPeers = newPeers; - } - catch (_e) { - console.error("Error parsing saved peers from cache:", _e); - } +function getCrypto() { + if (typeof window === "undefined") { + if (typeof self !== "undefined" && self.document === undefined) { + // inside a web worker + return crypto; } - if (!savedPeers.includes(host)) { - savedPeers.push(host); + else { + // inside node + return require("crypto").webcrypto; } - self.toolDb.store.put(constants_1.SAVED_PEERS_KEY, JSON.stringify(savedPeers), () => { - console.log("Saved peers to cache", savedPeers); - }); - }); + } + // browsers + return window.crypto; } -exports.default = addHost; +exports.default = getCrypto; -},{"./constants":3}],2:[function(require,module,exports){ +},{"crypto":365}],2:[function(require,module,exports){ +(function (global,Buffer){(function (){ "use strict"; +/* eslint-disable no-undef */ +/* eslint-disable global-require */ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const handleDraftsIndex_1 = __importDefault(require("./handleDraftsIndex")); -const handleLiveFeed_1 = __importDefault(require("./handleLiveFeed")); -const handleMatchesIndex_1 = __importDefault(require("./handleMatchesIndex")); -const reduxAction_1 = __importDefault(require("./reduxAction")); -function afterLogin() { - const currentDay = Math.floor(new Date().getTime() / (86400 * 1000)); - if (self.toolDb.user) { - (0, reduxAction_1.default)("SET_PUBKEY", self.toolDb.user.pubKey); - self.toolDb.store - .query(`:${self.toolDb.user.pubKey}.matches-`) - .then((matches) => { - (0, reduxAction_1.default)("SET_LOCAL_MATCHES_INDEX", matches); - }); - self.toolDb - .queryKeys(`:${self.toolDb.user.pubKey}.matches-`, false, 5000, true) - .then(handleMatchesIndex_1.default); - self.toolDb - .queryKeys(`:${self.toolDb.user.pubKey}.draft-`) - .then(handleDraftsIndex_1.default); - self.toolDb.getData(`privateMode`, true).then((settingPrivate) => { - (0, reduxAction_1.default)("SET_SETTINGS", { privateMode: settingPrivate === true }); - }); +exports.ToolDb = exports.handleSubscribe = exports.handleQuery = exports.handlePut = exports.handlePong = exports.handlePing = exports.handleGet = exports.handleCrdtPut = exports.handleCrdtGet = exports.handleCrdt = exports.ToolDbNetwork = exports.ToolDbNetworkAdapter = exports.verifyData = exports.saveKeysComb = exports.loadKeysComb = exports.importKey = exports.generateKeysComb = exports.generateKeyPair = exports.generateKeyFromPassword = exports.exportKey = exports.encryptWithPass = exports.encryptData = exports.encodeKeyString = exports.deriveSecret = exports.decryptWithPass = exports.decryptData = exports.decodeKeyString = exports.verifyMessage = exports.uint8ToBase64 = exports.toBase64 = exports.textRandom = exports.stringToArrayBuffer = exports.randomAnimal = exports.generateIv = exports.fromBase64 = exports.base64ToUint8 = exports.base64ToBinaryDocument = exports.base64ToBinaryChange = exports.base64ToArrayBuffer = exports.arrayBufferToString = exports.arrayBufferToBase64 = exports.signData = exports.sha256 = exports.sha1 = exports.uniq = exports.proofOfWork = void 0; +var elliptic_1 = __importDefault(require("elliptic")); +__exportStar(require("./types/tooldb"), exports); +__exportStar(require("./types/message"), exports); +global.Buffer = global.Buffer || require("buffer").Buffer; +if (typeof btoa === "undefined") { + global.btoa = function (str) { + return Buffer.from(str, "binary").toString("base64"); + }; +} +if (typeof atob === "undefined") { + global.atob = function (b64Encoded) { + return Buffer.from(b64Encoded, "base64").toString("binary"); + }; +} +if (typeof window === "undefined") { + // Only set global crypto if we are in node, not for web workers + if (typeof self !== "undefined" && self.document !== undefined) { + global.crypto = require("crypto").webcrypto; } - self.toolDb.addKeyListener(`matches-livefeed-${currentDay}`, handleLiveFeed_1.default); - self.toolDb.getData(`matches-livefeed-${currentDay}`); - self.toolDb.subscribeData("userids", true); - self.toolDb.subscribeData(`matches-livefeed-${currentDay}`); - self.toolDb.getData("hiddenDecks", true, 5000).then((hidden) => { - if (hidden) { - self.globalData.hiddenDecks = hidden; - (0, reduxAction_1.default)("SET_HIDDEN_DECKS", hidden); + global.ecp256 = new elliptic_1.default.ec("p256"); +} +else { + window.ecp256 = new elliptic_1.default.ec("p256"); +} +var proofOfWork_1 = require("./utils/proofOfWork"); +Object.defineProperty(exports, "proofOfWork", { enumerable: true, get: function () { return __importDefault(proofOfWork_1).default; } }); +var uniq_1 = require("./utils/uniq"); +Object.defineProperty(exports, "uniq", { enumerable: true, get: function () { return __importDefault(uniq_1).default; } }); +var sha1_1 = require("./utils/sha1"); +Object.defineProperty(exports, "sha1", { enumerable: true, get: function () { return __importDefault(sha1_1).default; } }); +var sha256_1 = require("./utils/sha256"); +Object.defineProperty(exports, "sha256", { enumerable: true, get: function () { return __importDefault(sha256_1).default; } }); +var signData_1 = require("./utils/signData"); +Object.defineProperty(exports, "signData", { enumerable: true, get: function () { return __importDefault(signData_1).default; } }); +var arrayBufferToBase64_1 = require("./utils/arrayBufferToBase64"); +Object.defineProperty(exports, "arrayBufferToBase64", { enumerable: true, get: function () { return __importDefault(arrayBufferToBase64_1).default; } }); +var arrayBufferToString_1 = require("./utils/arrayBufferToString"); +Object.defineProperty(exports, "arrayBufferToString", { enumerable: true, get: function () { return __importDefault(arrayBufferToString_1).default; } }); +var base64ToArrayBuffer_1 = require("./utils/base64ToArrayBuffer"); +Object.defineProperty(exports, "base64ToArrayBuffer", { enumerable: true, get: function () { return __importDefault(base64ToArrayBuffer_1).default; } }); +var base64ToBinaryChange_1 = require("./utils/base64ToBinaryChange"); +Object.defineProperty(exports, "base64ToBinaryChange", { enumerable: true, get: function () { return __importDefault(base64ToBinaryChange_1).default; } }); +var base64ToBinaryDocument_1 = require("./utils/base64ToBinaryDocument"); +Object.defineProperty(exports, "base64ToBinaryDocument", { enumerable: true, get: function () { return __importDefault(base64ToBinaryDocument_1).default; } }); +var base64ToUint8_1 = require("./utils/base64ToUint8"); +Object.defineProperty(exports, "base64ToUint8", { enumerable: true, get: function () { return __importDefault(base64ToUint8_1).default; } }); +var fromBase64_1 = require("./utils/fromBase64"); +Object.defineProperty(exports, "fromBase64", { enumerable: true, get: function () { return __importDefault(fromBase64_1).default; } }); +var generateIv_1 = require("./utils/generateIv"); +Object.defineProperty(exports, "generateIv", { enumerable: true, get: function () { return __importDefault(generateIv_1).default; } }); +var randomAnimal_1 = require("./utils/randomAnimal"); +Object.defineProperty(exports, "randomAnimal", { enumerable: true, get: function () { return __importDefault(randomAnimal_1).default; } }); +var stringToArrayBuffer_1 = require("./utils/stringToArrayBuffer"); +Object.defineProperty(exports, "stringToArrayBuffer", { enumerable: true, get: function () { return __importDefault(stringToArrayBuffer_1).default; } }); +var textRandom_1 = require("./utils/textRandom"); +Object.defineProperty(exports, "textRandom", { enumerable: true, get: function () { return __importDefault(textRandom_1).default; } }); +var toBase64_1 = require("./utils/toBase64"); +Object.defineProperty(exports, "toBase64", { enumerable: true, get: function () { return __importDefault(toBase64_1).default; } }); +var uint8ToBase64_1 = require("./utils/uint8ToBase64"); +Object.defineProperty(exports, "uint8ToBase64", { enumerable: true, get: function () { return __importDefault(uint8ToBase64_1).default; } }); +var verifyMessage_1 = require("./utils/verifyMessage"); +Object.defineProperty(exports, "verifyMessage", { enumerable: true, get: function () { return __importDefault(verifyMessage_1).default; } }); +var decodeKeyString_1 = require("./utils/crypto/decodeKeyString"); +Object.defineProperty(exports, "decodeKeyString", { enumerable: true, get: function () { return __importDefault(decodeKeyString_1).default; } }); +var decryptData_1 = require("./utils/crypto/decryptData"); +Object.defineProperty(exports, "decryptData", { enumerable: true, get: function () { return __importDefault(decryptData_1).default; } }); +var decryptWithPass_1 = require("./utils/crypto/decryptWithPass"); +Object.defineProperty(exports, "decryptWithPass", { enumerable: true, get: function () { return __importDefault(decryptWithPass_1).default; } }); +var deriveSecret_1 = require("./utils/crypto/deriveSecret"); +Object.defineProperty(exports, "deriveSecret", { enumerable: true, get: function () { return __importDefault(deriveSecret_1).default; } }); +var encodeKeyString_1 = require("./utils/crypto/encodeKeyString"); +Object.defineProperty(exports, "encodeKeyString", { enumerable: true, get: function () { return __importDefault(encodeKeyString_1).default; } }); +var encryptData_1 = require("./utils/crypto/encryptData"); +Object.defineProperty(exports, "encryptData", { enumerable: true, get: function () { return __importDefault(encryptData_1).default; } }); +var encryptWithPass_1 = require("./utils/crypto/encryptWithPass"); +Object.defineProperty(exports, "encryptWithPass", { enumerable: true, get: function () { return __importDefault(encryptWithPass_1).default; } }); +var exportKey_1 = require("./utils/crypto/exportKey"); +Object.defineProperty(exports, "exportKey", { enumerable: true, get: function () { return __importDefault(exportKey_1).default; } }); +var generateKeyFromPassword_1 = require("./utils/crypto/generateKeyFromPassword"); +Object.defineProperty(exports, "generateKeyFromPassword", { enumerable: true, get: function () { return __importDefault(generateKeyFromPassword_1).default; } }); +var generateKeyPair_1 = require("./utils/crypto/generateKeyPair"); +Object.defineProperty(exports, "generateKeyPair", { enumerable: true, get: function () { return __importDefault(generateKeyPair_1).default; } }); +var generateKeysComb_1 = require("./utils/crypto/generateKeysComb"); +Object.defineProperty(exports, "generateKeysComb", { enumerable: true, get: function () { return __importDefault(generateKeysComb_1).default; } }); +var importKey_1 = require("./utils/crypto/importKey"); +Object.defineProperty(exports, "importKey", { enumerable: true, get: function () { return __importDefault(importKey_1).default; } }); +var loadKeysComb_1 = require("./utils/crypto/loadKeysComb"); +Object.defineProperty(exports, "loadKeysComb", { enumerable: true, get: function () { return __importDefault(loadKeysComb_1).default; } }); +var saveKeysComb_1 = require("./utils/crypto/saveKeysComb"); +Object.defineProperty(exports, "saveKeysComb", { enumerable: true, get: function () { return __importDefault(saveKeysComb_1).default; } }); +var verifyData_1 = require("./utils/crypto/verifyData"); +Object.defineProperty(exports, "verifyData", { enumerable: true, get: function () { return __importDefault(verifyData_1).default; } }); +var networkAdapterBase_1 = require("./networkAdapterBase"); +Object.defineProperty(exports, "ToolDbNetworkAdapter", { enumerable: true, get: function () { return __importDefault(networkAdapterBase_1).default; } }); +var toolDbNetwork_1 = require("./toolDbNetwork"); +Object.defineProperty(exports, "ToolDbNetwork", { enumerable: true, get: function () { return __importDefault(toolDbNetwork_1).default; } }); +var handleCrdt_1 = require("./messageHandlers/handleCrdt"); +Object.defineProperty(exports, "handleCrdt", { enumerable: true, get: function () { return __importDefault(handleCrdt_1).default; } }); +var handleCrdtGet_1 = require("./messageHandlers/handleCrdtGet"); +Object.defineProperty(exports, "handleCrdtGet", { enumerable: true, get: function () { return __importDefault(handleCrdtGet_1).default; } }); +var handleCrdtPut_1 = require("./messageHandlers/handleCrdtPut"); +Object.defineProperty(exports, "handleCrdtPut", { enumerable: true, get: function () { return __importDefault(handleCrdtPut_1).default; } }); +var handleGet_1 = require("./messageHandlers/handleGet"); +Object.defineProperty(exports, "handleGet", { enumerable: true, get: function () { return __importDefault(handleGet_1).default; } }); +var handlePing_1 = require("./messageHandlers/handlePing"); +Object.defineProperty(exports, "handlePing", { enumerable: true, get: function () { return __importDefault(handlePing_1).default; } }); +var handlePong_1 = require("./messageHandlers/handlePong"); +Object.defineProperty(exports, "handlePong", { enumerable: true, get: function () { return __importDefault(handlePong_1).default; } }); +var handlePut_1 = require("./messageHandlers/handlePut"); +Object.defineProperty(exports, "handlePut", { enumerable: true, get: function () { return __importDefault(handlePut_1).default; } }); +var handleQuery_1 = require("./messageHandlers/handleQuery"); +Object.defineProperty(exports, "handleQuery", { enumerable: true, get: function () { return __importDefault(handleQuery_1).default; } }); +var handleSubscribe_1 = require("./messageHandlers/handleSubscribe"); +Object.defineProperty(exports, "handleSubscribe", { enumerable: true, get: function () { return __importDefault(handleSubscribe_1).default; } }); +var tooldb_1 = require("./tooldb"); +Object.defineProperty(exports, "ToolDb", { enumerable: true, get: function () { return __importDefault(tooldb_1).default; } }); + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("buffer").Buffer) +},{"./messageHandlers/handleCrdt":5,"./messageHandlers/handleCrdtGet":6,"./messageHandlers/handleCrdtPut":7,"./messageHandlers/handleGet":9,"./messageHandlers/handlePing":10,"./messageHandlers/handlePong":11,"./messageHandlers/handlePut":12,"./messageHandlers/handleQuery":13,"./messageHandlers/handleSubscribe":14,"./networkAdapterBase":15,"./toolDbNetwork":24,"./tooldb":32,"./types/message":33,"./types/tooldb":34,"./utils/arrayBufferToBase64":35,"./utils/arrayBufferToString":37,"./utils/base64ToArrayBuffer":38,"./utils/base64ToBinaryChange":39,"./utils/base64ToBinaryDocument":40,"./utils/base64ToUint8":41,"./utils/crypto/decodeKeyString":43,"./utils/crypto/decryptData":44,"./utils/crypto/decryptWithPass":45,"./utils/crypto/deriveSecret":46,"./utils/crypto/encodeKeyString":47,"./utils/crypto/encryptData":48,"./utils/crypto/encryptWithPass":49,"./utils/crypto/exportKey":50,"./utils/crypto/generateKeyFromPassword":51,"./utils/crypto/generateKeyPair":52,"./utils/crypto/generateKeysComb":53,"./utils/crypto/importKey":54,"./utils/crypto/loadKeysComb":55,"./utils/crypto/saveKeysComb":56,"./utils/crypto/verifyData":57,"./utils/fromBase64":58,"./utils/generateIv":59,"./utils/proofOfWork":64,"./utils/randomAnimal":65,"./utils/sha1":66,"./utils/sha256":67,"./utils/signData":68,"./utils/stringToArrayBuffer":69,"./utils/textRandom":70,"./utils/toBase64":71,"./utils/uint8ToBase64":72,"./utils/uniq":73,"./utils/verifyMessage":74,"buffer":336,"crypto":365,"elliptic":109}],3:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var automerge_1 = __importDefault(require("automerge")); +function loadCrdtDocument(key, doDefault) { + var _this = this; + if (doDefault === void 0) { doDefault = true; } + return new Promise(function (resolve, reject) { + if (_this.documents[key]) { + resolve(_this.documents[key]); } - }); - self.toolDb - .getData("userids", true, 5000) - .then((data) => { - let newest = ""; - let newestDate = 0; - Object.keys(data) - .filter((k) => k && k !== "undefined") - .forEach((uuid) => { - if (data[uuid] > newestDate) { - newestDate = data[uuid]; - newest = uuid; - } - self.toolDb.addKeyListener(self.toolDb.getUserNamespacedKey(`${uuid}-displayname`), (msg) => { - if (msg.type === "put") { - (0, reduxAction_1.default)("SET_UUID_DISPLAYNAME", { - displayName: msg.v.displayName, - uuid, - }); - } - }); - self.toolDb.subscribeData(`${uuid}-displayname`, true); - self.toolDb.addKeyListener(self.toolDb.getUserNamespacedKey(`${uuid}-cards`), (msg) => { - if (msg.type === "put") { - (0, reduxAction_1.default)("SET_UUID_CARDS_DATA", { cards: msg.v, uuid }); - } - }); - self.toolDb.subscribeData(`${uuid}-cards`, true); - self.toolDb.addKeyListener(self.toolDb.getUserNamespacedKey(`${uuid}-inventory`), (msg) => { - if (msg.type === "put") { - (0, reduxAction_1.default)("SET_UUID_INVENTORY_DATA", { - inventory: msg.v, - uuid, - }); + else { + _this.store.get(key + ".crdt", function (err, data) { + var currentDoc = null; + if (data) { + // De-serealize stored crdt document + // console.log("data", typeof data, data); + var loaded_1 = data; + if (typeof data === "string") { + var split = data.split(","); + loaded_1 = new Uint8Array(split.length); + split.forEach(function (s, i) { + loaded_1[i] = parseInt(s); + }); + } + currentDoc = automerge_1.default.load(loaded_1); + // console.log("CRDT LOADED", key, currentDoc); } - }); - self.toolDb.subscribeData(`${uuid}-inventory`, true); - self.toolDb.addKeyListener(self.toolDb.getUserNamespacedKey(`${uuid}-rank`), (msg) => { - if (msg.type === "put") { - (0, reduxAction_1.default)("SET_UUID_RANK_DATA", { rank: msg.v, uuid }); + else { + // console.log("CRDT not found for", key); + if (doDefault) { + currentDoc = automerge_1.default.init(); + } } + resolve(currentDoc); }); - self.toolDb.subscribeData(`${uuid}-rank`, true); - self.globalData.currentUUID = newest; - (0, reduxAction_1.default)("SET_UUID", newest); - }); - }) - .catch(console.warn); + } + }); } -exports.default = afterLogin; +exports.default = loadCrdtDocument; -},{"./handleDraftsIndex":13,"./handleLiveFeed":14,"./handleMatchesIndex":15,"./reduxAction":21}],3:[function(require,module,exports){ +},{"automerge":100}],4:[function(require,module,exports){ "use strict"; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; Object.defineProperty(exports, "__esModule", { value: true }); -exports.SERVERS_KEY = exports.SAVED_PEERS_KEY = exports.DEFAULT_PEERS = void 0; -const mtgatool_db_1 = require("mtgatool-db"); -/* eslint-disable import/prefer-default-export */ -exports.DEFAULT_PEERS = [ - "ccdde0f639db3ccb18ed2d48ed405323eac3ce86762923c1010796433e9a392dec3771400f524d4f0a466c0701cad99bbd8b509df3a467c8ca76fab7dc5504bb", -]; -exports.SAVED_PEERS_KEY = (0, mtgatool_db_1.sha1)("-.saved-peers.-"); -exports.SERVERS_KEY = (0, mtgatool_db_1.sha1)("-.servers.-"); +var _1 = require("."); +function logger(arg0, arg1, arg2, arg3, arg4, arg5) { + if (this.options.debug) { + var isNode = typeof window === "undefined" ? self.document !== undefined : false; + var title = isNode + ? this.options.storageName + : "%c" + this.options.storageName; + var style = isNode + ? "" + : "background: #" + + (0, _1.sha1)(this.options.storageName).slice(-6) + + "; padding: 2px; color: black"; + console.log.apply(console, __spreadArray([title, + style], [arg0, arg1, arg2, arg3, arg4, arg4, arg5].slice(0, arguments.length), false)); + } +} +exports.default = logger; -},{"mtgatool-db":291}],4:[function(require,module,exports){ +},{".":2}],5:[function(require,module,exports){ "use strict"; -/* eslint-disable no-restricted-globals */ Object.defineProperty(exports, "__esModule", { value: true }); -function doFunction(msgId, fname, args) { - return self.toolDb - .doFunction(fname, args) - .then((value) => { - self.postMessage({ type: `${msgId}_OK`, value }); - }) - .catch((err) => { - self.postMessage({ type: "LOGIN_ERR", err }); +var __1 = require(".."); +function handleCrdt(message, remotePeerId) { + var key = message.key; + this.triggerKeyListener(key, message); + // OOHH THE TYPECAST PAIN + // This works but the hacking is awful, we need a better solution for storing the crdts + var savedDoc = (0, __1.base64ToBinaryDocument)(message.doc); + this.store.put(key + ".crdt", savedDoc, function (err, data) { + // }); } -exports.default = doFunction; +exports.default = handleCrdt; -},{}],5:[function(require,module,exports){ +},{"..":2}],6:[function(require,module,exports){ "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.beginDataQuery = exports.limitRecord = void 0; -/* eslint-disable no-restricted-globals */ -/* eslint-disable no-bitwise */ -/* eslint-disable radix */ -const automerge_1 = __importDefault(require("automerge")); -const mtgatool_db_1 = require("mtgatool-db"); -function limitRecord(record, limit) { - const newRecord = {}; - Object.keys(record) - .map((k) => { - const v = record[k]; - return { - k, - v, - }; - }) - .sort((a, b) => { - if (a.v > b.v) - return -1; - if (b.v > a.v) - return 1; - return 0; - }) - .splice(0, limit) - .forEach((d) => { - newRecord[d.k] = d.v; - }); - return newRecord; -} -exports.limitRecord = limitRecord; -function beginDataQuery(_day, _eventId) { - let crdt = automerge_1.default.init({}); - const data = {}; - let queriedIds = []; - function continnuousCheck() { - self.postMessage({ - type: `EXPLORE_DATA_QUERY_STATE`, - value: { - foundKeys: Object.keys(crdt).length, - queriedKeys: queriedIds.length, - savedKeys: Object.keys(data).length, - loadingPercent: Math.floor((queriedIds.length / Object.keys(crdt).length) * 100), - }, - }); - if (Object.keys(crdt).length === queriedIds.length) { - self.postMessage({ - type: `EXPLORE_DATA_QUERY`, - value: data, +var automerge_1 = __importDefault(require("automerge")); +var __1 = require(".."); +function handleCrdtGet(message, remotePeerId) { + var _this = this; + this.loadCrdtDocument(message.key, false).then(function (currentDoc) { + if (currentDoc) { + var saved = automerge_1.default.save(currentDoc || automerge_1.default.init()); + _this.network.sendToClientId(remotePeerId, { + type: "crdt", + id: message.id, + key: message.key, + to: [], + doc: (0, __1.uint8ToBase64)(saved), }); } - if (Object.keys(crdt).length === 0) { - setTimeout(() => { - continnuousCheck(); - }, 500); + else { + _this.logger("Local key not found, relay", JSON.stringify(message)); + _this.network.sendToAll(message, true); } - Object.keys(crdt) - .filter((k) => !queriedIds.includes(k)) - .splice(0, 1) - .forEach((id) => { - queriedIds = [...queriedIds, id]; - self.toolDb.getData(id).then((d) => { - data[id] = d; - continnuousCheck(); - }); - }); - } - const handleExploreData = (msg) => { - if (msg && msg.type === "crdt") { - const doc = automerge_1.default.load((0, mtgatool_db_1.base64ToBinaryDocument)(msg.doc)); + }); +} +exports.default = handleCrdtGet; + +},{"..":2,"automerge":100}],7:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var automerge_1 = __importDefault(require("automerge")); +var __1 = require(".."); +var toolDbVerificationWrapper_1 = __importDefault(require("../toolDbVerificationWrapper")); +function handleCrdtPut(message, remotePeerId) { + var _this = this; + // key = aggregated, final value + // key.crdt = automerge doc with changes + // const writeStart = new Date().getTime(); + toolDbVerificationWrapper_1.default.call(this, message).then(function (value) { + // console.log("CRDT Verification wrapper result: ", value); + if (value === __1.VerifyResult.Verified) { + var key_1 = message.k; + var data = []; try { - crdt = automerge_1.default.merge(crdt, doc); + data = JSON.parse(message.v); } catch (e) { - console.warn(e); + // } + var changes_1 = data.map(__1.base64ToBinaryChange); + _this.loadCrdtDocument(key_1).then(function (currentDoc) { + // if (currentDoc) { + // console.log( + // "loaded", + // key, + // currentDoc, + // Automerge.getHistory(currentDoc) + // ); + // } + var newDoc = automerge_1.default.init(); + try { + newDoc = automerge_1.default.applyChanges(currentDoc || automerge_1.default.init(), changes_1)[0]; + } + catch (e) { + try { + newDoc = automerge_1.default.applyChanges(automerge_1.default.init(), changes_1)[0]; + } + catch (ee) { + _this.logger(ee); + } + } + // if (newDoc) { + // console.log( + // "new document changes:", + // Automerge.getHistory(newDoc), + // "final: ", + // newDoc + // ); + // } + // persist + _this.documents[key_1] = newDoc; + // OOHH THE TYPECAST PAIN + // This works but the hacking is awful, we need a better solution for storing the crdts + var savedDoc = automerge_1.default.save(newDoc); + _this.store.put(key_1 + ".crdt", savedDoc, function (err, data) { + // const writeEnd = new Date().getTime(); + // console.log("CRDT write: ", (writeEnd - writeStart) / 1000); + }); + var crdtMessage = { + type: "crdt", + key: key_1, + id: message.id, + to: [], + doc: (0, __1.uint8ToBase64)(savedDoc), + }; + _this.triggerKeyListener(key_1, crdtMessage); + // relay to other servers + // !!! + _this.network.sendToAll(crdtMessage, true); + }); } - }; - function queryForEvent(ev) { - for (let i = 0; i < _day; i += 1) { - const currentDay = Math.floor(new Date().getTime() / (86400 * 1000)); - const queryKey = `explore-${currentDay - i}-${ev}`; - self.toolDb.addKeyListener(queryKey, handleExploreData); - self.toolDb.subscribeData(queryKey); + else { + console.log("unverified message", value, message); } - } - if (_eventId === "aggregated-standard") { - queryForEvent("Ladder"); - queryForEvent("Play"); - queryForEvent("Constructed_BestOf3"); - queryForEvent("Constructed_Event_2020"); - queryForEvent("Constructed_Event_2021"); - queryForEvent("Constructed_Event_v2"); - queryForEvent("Traditional_Cons_Event_2020"); - queryForEvent("Traditional_Cons_Event_2021"); - queryForEvent("Traditional_Cons_Event_2022"); - queryForEvent("Traditional_Cons_Event_2023"); - queryForEvent("Traditional_Ladder"); - queryForEvent("Standard_Challenge_20230421"); - } - else if (_eventId === "aggregated-historic") { - queryForEvent("Historic_Ladder"); - queryForEvent("Historic_Play"); - queryForEvent("Historic_Event_v2"); - queryForEvent("Historic_Event"); - queryForEvent("Traditional_Historic_Event"); - queryForEvent("Traditional_Historic_Play"); - queryForEvent("Traditional_Historic_Ladder"); - } - else if (_eventId === "aggregated-alchemy") { - queryForEvent("Alchemy_Ladder"); - queryForEvent("Alchemy_Play"); - queryForEvent("Alchemy_Event"); - queryForEvent("Traditional_Alchemy_Event_2022"); - queryForEvent("Traditional_Alchemy_Event_2023"); - queryForEvent("Traditional_Alchemy_Event"); - queryForEvent("Traditional_Alchemy_Play"); - queryForEvent("Traditional_Alchemy_Ladder"); - } - else if (_eventId === "aggregated-explorer") { - queryForEvent("Explorer_Ladder"); - queryForEvent("Explorer_Play"); - queryForEvent("Explorer_Event"); - queryForEvent("Explorer_Event_v2"); - queryForEvent("Traditional_Explorer_Event"); - queryForEvent("Traditional_Explorer_Play"); - queryForEvent("Traditional_Explorer_Ladder"); - } - else { - queryForEvent(_eventId); - } - continnuousCheck(); -} -exports.beginDataQuery = beginDataQuery; - -},{"automerge":84,"mtgatool-db":291}],6:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -function getConnectionData() { - const networkModule = self.toolDb.network; - const connectionData = Object.keys(networkModule.clientToSend).map((peerId) => { - const serverPeerData = networkModule.serverPeerData[peerId]; - const peerData = self.toolDb.peers[peerId]; - const host = peerData === null || peerData === void 0 ? void 0 : peerData.host; - const peerHost = !host || host === "127.0.0.1" ? peerId.slice(-20) : host; - const socket = networkModule.clientSocket[peerId]; - return { - peerId, - peerData, - serverPeerData: serverPeerData, - host: (serverPeerData === null || serverPeerData === void 0 ? void 0 : serverPeerData.name) || peerHost, - readyState: socket === null || socket === void 0 ? void 0 : socket.readyState, - isConnected: (socket === null || socket === void 0 ? void 0 : socket.readyState) === socket.OPEN, - }; - }); - self.postMessage({ type: `CONNECTION_DATA`, value: connectionData }); -} -exports.default = getConnectionData; - -},{}],7:[function(require,module,exports){ -"use strict"; -/* eslint-disable no-restricted-globals */ -Object.defineProperty(exports, "__esModule", { value: true }); -function getCrdt(msgId, key, userNamespaced, timeoutMs = 5000) { - return self.toolDb - .getCrdt(key, userNamespaced, timeoutMs) - .then((value) => { - self.postMessage({ type: `${msgId}_OK`, value }); - }) - .catch((err) => { - self.postMessage({ type: `${msgId}_ERR`, err }); }); } -exports.default = getCrdt; +exports.default = handleCrdtPut; -},{}],8:[function(require,module,exports){ +},{"..":2,"../toolDbVerificationWrapper":31,"automerge":100}],8:[function(require,module,exports){ "use strict"; -/* eslint-disable no-restricted-globals */ Object.defineProperty(exports, "__esModule", { value: true }); -function getData(msgId, key, userNamespaced, timeoutMs = 5000) { - return self.toolDb - .getData(key, userNamespaced, timeoutMs) - .then((value) => { - self.postMessage({ type: `${msgId}_OK`, value }); - }) - .catch((err) => { - self.postMessage({ type: `${msgId}_ERR`, err }); - }); +function handleFunction(message, remotePeerId) { + var _this = this; + // executes the given function by message on the server if it exists + if (this.functions[message.function]) { + try { + this.functions[message.function](message.args) + .then(function (ret) { + var messageReturn = { + return: ret, + type: "functionReturn", + id: message.id, + code: "OK", + to: [], + }; + _this.network.sendToClientId(remotePeerId, messageReturn); + }) + .catch(function (e) { + var messageReturn = { + return: JSON.stringify(e), + type: "functionReturn", + id: message.id, + code: "ERR", + to: [], + }; + _this.network.sendToClientId(remotePeerId, messageReturn); + }); + } + catch (e) { + // something went wrong, nothing to do here + // We might want to return the exception to the client? + var messageReturn = { + return: e.toString(), + type: "functionReturn", + id: message.id, + code: "ERR", + to: [], + }; + this.network.sendToClientId(remotePeerId, messageReturn); + } + } + else { + // function not found + var messageReturn = { + return: "Function not found", + type: "functionReturn", + id: message.id, + code: "NOT_FOUND", + to: [], + }; + this.network.sendToClientId(remotePeerId, messageReturn); + } } -exports.default = getData; +exports.default = handleFunction; },{}],9:[function(require,module,exports){ "use strict"; -/* eslint-disable no-restricted-globals */ +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; Object.defineProperty(exports, "__esModule", { value: true }); -function getDataLocal(msgId, key) { - return new Promise((resolve) => { - self.toolDb.store.get(key, (err, data) => { - if (err) { - self.postMessage({ type: `${msgId}_ERR`, err }); - } - else if (data) { - try { - const json = JSON.parse(data); - self.postMessage({ type: `${msgId}_OK`, value: json.v }); - resolve(json.v); - } - catch (_e) { - self.postMessage({ type: `${msgId}_ERR`, err: _e }); - } +function handleGet(message, remotePeerId) { + var _this = this; + this.store.get(message.key, function (err, data) { + if (data) { + try { + // Use the id of the get so the other client knows we are replying + var oldData = __assign(__assign({ type: "put" }, JSON.parse(data)), { id: message.id, to: [] }); + _this.network.sendToClientId(remotePeerId, oldData); } - else { - self.postMessage({ type: `${msgId}_ERR`, err: "No data" }); + catch (e) { + // socket.send(data); + // do nothing } - }); + } + else { + _this.logger("Local key not found, relay", JSON.stringify(message)); + _this.network.sendToAll(message, true); + } }); } -exports.default = getDataLocal; +exports.default = handleGet; },{}],10:[function(require,module,exports){ "use strict"; @@ -377,34723 +457,12352 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getMatchesData = exports.getMatchesDataLocal = exports.convertDbMatchToData = void 0; -const getRankFilterVal_1 = __importDefault(require("./getRankFilterVal")); -function convertDbMatchToData(match) { - const { internalMatch } = match; - return { - uuid: match.playerId, - matchId: match.matchId, - internalMatch: match.internalMatch, - playerDeckName: internalMatch.player.name, - timestamp: match.timestamp, - duration: match.duration, - win: match.playerWins > match.playerLosses, - eventId: match.eventId, - playerDeckColors: match.playerDeckColors, - oppDeckColors: match.oppDeckColors, - playerWins: match.playerWins, - playerLosses: match.playerLosses, - rank: (0, getRankFilterVal_1.default)(internalMatch.player.rank), - }; -} -exports.convertDbMatchToData = convertDbMatchToData; -function getLocalData(key) { - return new Promise((resolve) => { - self.toolDb.store.get(key, (err, data) => { - if (err) { - resolve(undefined); - } - else if (data) { - try { - const json = JSON.parse(data); - resolve(json.v); - } - catch (_e) { - resolve(undefined); - } - } - else { - resolve(undefined); - } - }); - }); -} -function getMatchesDataLocal(msgId, matchesIds, uuid) { - const promises = matchesIds.map((id) => { - return getLocalData(id); - }); - Promise.all(promises) - .then((matches) => matches.filter((m) => m).map((m) => convertDbMatchToData(m))) - .then((data) => { - self.postMessage({ - type: `${msgId}_OK`, - value: data.filter((m) => (uuid ? m.uuid === uuid : true)), - }); - }); -} -exports.getMatchesDataLocal = getMatchesDataLocal; -function getMatchesData(msgId, matchesIds, uuid, updateCallback) { - const matchesIndex = [...new Set([...(matchesIds || [])])]; - let saved = 0; - let timeout = null; - let lastUpdate = new Date().getTime(); - function updateState() { - timeout = null; - if (updateCallback) { - updateCallback(matchesIndex.length, saved); - } - if (saved === matchesIndex.length && matchesIndex.length > 0) { - getMatchesDataLocal(msgId, matchesIndex, uuid); - } - } - function debounceUpdateState() { - if (timeout) - clearTimeout(timeout); - if (new Date().getTime() - lastUpdate > 1000) { - lastUpdate = new Date().getTime(); - updateState(); - } - timeout = setTimeout(updateState, 100); - } - // Fetch any match we dont have locally - matchesIndex.forEach((id) => { - self.toolDb.store.get(id, (err) => { - if (!err) { - saved += 1; - debounceUpdateState(); - } - else { - self.toolDb.getData(id, false, 2000).finally(() => { - saved += 1; - debounceUpdateState(); - }); - } - }); - }); - updateState(); -} -exports.getMatchesData = getMatchesData; - -},{"./getRankFilterVal":11}],11:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RANK_MYTHIC = exports.RANK_DIAMOND = exports.RANK_PLATINUM = exports.RANK_GOLD = exports.RANK_SILVER = exports.RANK_BRONZE = void 0; -exports.RANK_BRONZE = 1; -exports.RANK_SILVER = 2; -exports.RANK_GOLD = 4; -exports.RANK_PLATINUM = 8; -exports.RANK_DIAMOND = 16; -exports.RANK_MYTHIC = 32; -function getRankFilterVal(rank) { - let ret = 0; - switch (rank) { - case "Unranked": - ret = 0; - break; - case "Bronze": - ret = exports.RANK_BRONZE; - break; - case "Silver": - ret = exports.RANK_SILVER; - break; - case "Gold": - ret = exports.RANK_GOLD; - break; - case "Platinum": - ret = exports.RANK_PLATINUM; - break; - case "Diamond": - ret = exports.RANK_DIAMOND; - break; - case "Mythic": - ret = exports.RANK_MYTHIC; - break; - default: - break; +var verifyPeer_1 = __importDefault(require("../utils/verifyPeer")); +function handlePing(message, remotePeerId) { + var _this = this; + if (!this.isConnected) { + this.isConnected = true; + this.onConnect(); } - return ret; -} -exports.default = getRankFilterVal; - -},{}],12:[function(require,module,exports){ -"use strict"; -/* eslint-disable no-restricted-globals */ -Object.defineProperty(exports, "__esModule", { value: true }); -const mtgatool_db_1 = require("mtgatool-db"); -function getSaveKeysJson() { - return new Promise((resolve) => { - if (self.toolDb.user) { - (0, mtgatool_db_1.saveKeysComb)(self.toolDb.user.keys.signKeys, self.toolDb.user.keys.encryptionKeys).then((keys) => { - var _a; - const saveKeys = { keys: keys, userName: ((_a = self.toolDb.user) === null || _a === void 0 ? void 0 : _a.name) || "" }; - self.postMessage({ type: `SAVE_KEYS_JSON`, value: saveKeys }); - resolve(saveKeys); + (0, verifyPeer_1.default)(message.peer).then(function (verified) { + // Verify integrity and topic + if (verified && message.peer.topic === _this.options.topic) { + // Add this peer to our list of peers + _this.peers[message.peer.pubkey] = message.peer; + _this.network.sendToClientId(remotePeerId, { + type: "pong", + isServer: _this.options.server, + clientId: _this.network.getClientAddress(), + to: [], + servers: _this.serverPeers, + id: message.id, }); + _this.onPeerConnect(message.peer.pubkey); } else { - resolve(null); + _this.logger("Blocked a remote peer from joining; ", message); + // Drop connection here! } }); } -exports.default = getSaveKeysJson; +exports.default = handlePing; -},{"mtgatool-db":291}],13:[function(require,module,exports){ +},{"../utils/verifyPeer":75}],11:[function(require,module,exports){ "use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -const reduxAction_1 = __importDefault(require("./reduxAction")); -function handleDraftsIndex(draftsIndex) { - self.globalData.draftsIndex = [ - ...new Set([...self.globalData.draftsIndex, ...(draftsIndex || [])]), - ]; - console.log("handleDraftsIndex", self.globalData.draftsIndex); - // Fetch any match we dont have locally - self.globalData.draftsIndex.forEach((id) => { - self.toolDb.store.get(id, (err, data) => { - if (!data) { - self.toolDb.getData(id, false, 2000); - } +var verifyPeer_1 = __importDefault(require("../utils/verifyPeer")); +function handlePong(message, remotePeerId) { + var _this = this; + if (!this.isConnected) { + this.isConnected = true; + this.onConnect(); + } + if (message.servers) { + message.servers.forEach(function (peer) { + (0, verifyPeer_1.default)(peer).then(function (verified) { + // Verify integrity and topic + if (verified && peer.topic === _this.options.topic) { + // Add this peer to our list of peers + _this.peers[peer.pubkey] = __assign(__assign({}, peer), { isServer: true }); + } + }); }); - }); - (0, reduxAction_1.default)("SET_DRAFTS_INDEX", self.globalData.draftsIndex); + } + this.onPeerConnect(message.clientId); } -exports.default = handleDraftsIndex; +exports.default = handlePong; -},{"./reduxAction":21}],14:[function(require,module,exports){ +},{"../utils/verifyPeer":75}],12:[function(require,module,exports){ "use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -const automerge_1 = __importDefault(require("automerge")); -const mtgatool_db_1 = require("mtgatool-db"); -const reduxAction_1 = __importDefault(require("./reduxAction")); -function handleLiveFeed(msg) { - console.log("Key Listener live feed ", msg); - if (msg && msg.type === "crdt") { - if (self.globalData.liveFeed) { - const doc = automerge_1.default.load((0, mtgatool_db_1.base64ToBinaryDocument)(msg.doc)); - try { - self.globalData.liveFeed = automerge_1.default.merge(automerge_1.default.init(), doc); - } - catch (e) { - console.warn(e); - } - const filteredLiveFeed = Object.keys(self.globalData.liveFeed) - .sort((a, b) => { - if (self.globalData.liveFeed[a] > self.globalData.liveFeed[b]) - return -1; - if (self.globalData.liveFeed[a] < self.globalData.liveFeed[b]) - return 1; - return 0; - }) - .slice(0, 10); - (0, reduxAction_1.default)("SET_LIVE_FEED", filteredLiveFeed); - // Fetch any match we dont have locally - filteredLiveFeed.forEach((id) => { - self.toolDb.store.get(id, (err, data) => { - if (!data) { - self.toolDb.getData(id, false).then((match) => { - (0, reduxAction_1.default)("SET_LIVE_FEED_MATCH", { key: id, match: match }); +var __1 = require(".."); +var toolDbVerificationWrapper_1 = __importDefault(require("../toolDbVerificationWrapper")); +function handlePut(message, remotePeerId) { + var _this = this; + toolDbVerificationWrapper_1.default.call(this, message).then(function (value) { + // console.log("Verification wrapper result: ", value, message.k); + if (value === __1.VerifyResult.Verified) { + // relay to other servers !!! + _this.network.sendToAll(message, true); + _this.store.get(message.k, function (err, oldData) { + if (oldData) { + var parsedOldData = __assign({ type: "put", to: [] }, JSON.parse(oldData)); + if (parsedOldData.t < message.t) { + var key = message.k; + _this.triggerKeyListener(key, message); + _this.store.put(message.k, JSON.stringify(message), function (err, data) { + // }); } else { - (0, reduxAction_1.default)("SET_LIVE_FEED_MATCH", { - key: id, - match: JSON.parse(data).v, - }); + var key = message.k; + _this.triggerKeyListener(key, parsedOldData); } - }); + // } else if (this.options.debug) { + // console.log( + // `${message.k} has old data, but its newer. old ${parsedOldData.t} < new ${message.t}` + // ); + // } + } + else { + var key = message.k; + _this.triggerKeyListener(key, message); + _this.store.put(message.k, JSON.stringify(message), function (err, data) { + // + }); + } }); } - } + else { + console.warn("unverified message: ", value, message); + } + }); } -exports.default = handleLiveFeed; +exports.default = handlePut; -},{"./reduxAction":21,"automerge":84,"mtgatool-db":291}],15:[function(require,module,exports){ +},{"..":2,"../toolDbVerificationWrapper":31}],13:[function(require,module,exports){ "use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -const getMatchesData_1 = require("./getMatchesData"); -const reduxAction_1 = __importDefault(require("./reduxAction")); -function handleMatchesIndex(matchesIds) { - if (matchesIds) { - (0, reduxAction_1.default)("SET_REMOTE_MATCHES_INDEX", matchesIds); - (0, getMatchesData_1.getMatchesData)("MATCHES_DATA", matchesIds, self.globalData.currentUUID, (total, saved) => { - (0, reduxAction_1.default)("SET_MATCHES_FETCH_STATE", { - total, - saved, - }); - }); - } -} -exports.default = handleMatchesIndex; - -},{"./getMatchesData":10,"./reduxAction":21}],16:[function(require,module,exports){ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -const mtgatool_db_1 = require("mtgatool-db"); -const afterLogin_1 = __importDefault(require("./afterLogin")); -const reduxAction_1 = __importDefault(require("./reduxAction")); -function keysLogin(username, keys) { - return new Promise((resolve, reject) => { - self.toolDb - .getData(`==${username}`, false, 5000) - .then((user) => { - if (user) { - (0, mtgatool_db_1.loadKeysComb)(keys).then((importedKeys) => { - if (importedKeys) { - (0, mtgatool_db_1.exportKey)("spki", importedKeys.signKeys.publicKey) - .then((skpub) => (0, mtgatool_db_1.encodeKeyString)(skpub)) - .then((pubKey) => { - if (pubKey === user.keys.skpub) { - self.toolDb.keysSignIn(importedKeys, username).then(() => { - self.postMessage({ type: "LOGIN_OK" }); - (0, reduxAction_1.default)("SET_PUBKEY", pubKey); - (0, afterLogin_1.default)(); - resolve(); - }); - } - else { - reject(new Error("Public key does not match!")); - } - }); - } - else { - reject(new Error("Something went wrong when importing the keys")); - } - }); - } - else { - reject(new Error("Could not find user to validate")); - } +function handleQuery(message, remotePeerId) { + var _this = this; + if (message.key.length < 3) + return; + this.store.query(message.key).then(function (keys) { + _this.network.sendToClientId(remotePeerId, { + type: "queryAck", + id: message.id, + to: [], + keys: keys, }); }); + if (this.options.server) { + this.network.sendToAll(message, true); + } } -exports.default = keysLogin; +exports.default = handleQuery; -},{"./afterLogin":2,"./reduxAction":21,"mtgatool-db":291}],17:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ "use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); }; -Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -const afterLogin_1 = __importDefault(require("./afterLogin")); -const reduxAction_1 = __importDefault(require("./reduxAction")); -function login(username, password) { - return self.toolDb - .signIn(username, password) - .then((keys) => { - var _a; - self.toolDb.putData("username", username, true); - (0, afterLogin_1.default)(); - self.postMessage({ type: "LOGIN_OK" }); - (0, reduxAction_1.default)("SET_PUBKEY", (_a = self.toolDb.user) === null || _a === void 0 ? void 0 : _a.pubKey); - return keys; - }) - .catch((err) => { - self.postMessage({ type: "LOGIN_ERR", err }); - }); -} -exports.default = login; - -},{"./afterLogin":2,"./reduxAction":21}],18:[function(require,module,exports){ -"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -/* eslint-disable no-param-reassign */ -const automerge_1 = __importDefault(require("automerge")); -async function pushToExplore(key, match) { - // Create CRDT document with the new match added to it - try { - const docInit = automerge_1.default.init(); - const { eventId } = match; - if (!eventId.includes("NPE_") && !eventId.includes("ColorChallenge_")) { - const newDocument = automerge_1.default.change(docInit, (doc) => { - doc[key] = new Date(match.internalMatch.date).getTime(); +var automerge_1 = __importDefault(require("automerge")); +var __1 = require(".."); +function handleSubscribe(message, remotePeerId) { + var _this = this; + if (remotePeerId) { + var subId = remotePeerId + "-" + message.key; + if (!this.subscriptions.includes(subId)) { + this.subscriptions.push(subId); + this.addKeyListener(message.key, function (msg) { + if ((msg.type === "put" || msg.type === "crdt") && remotePeerId) { + // We do not reply to the socket directly + // instead we use the client id, in case the socket reconnects + _this.network.sendToClientId(remotePeerId, msg); + } }); - const currentDay = Math.floor(new Date().getTime() / (86400 * 1000)); - self.toolDb - .putCrdt(`explore-${currentDay}-${eventId}`, automerge_1.default.getChanges(docInit, newDocument), false) - .catch(console.error); } } - catch (e) { - console.warn(e); - } + // basically the exact same as GET, below + this.store.get(message.key, function (err, data) { + if (data) { + try { + var oldData = __assign(__assign({}, JSON.parse(data)), { id: message.id, to: [] }); + _this.network.sendToClientId(remotePeerId, oldData); + } + catch (e) { + // do nothing + } + } + }); + this.loadCrdtDocument(message.key, false).then(function (doc) { + // console.log("Load crdt from subscribe", message.key, doc); + if (doc) { + var savedDoc = automerge_1.default.save(doc); + var msg = { + type: "crdt", + key: message.key, + to: [], + id: (0, __1.textRandom)(10), + doc: (0, __1.uint8ToBase64)(savedDoc), + }; + _this.network.sendToClientId(remotePeerId, msg); + } + }); } -exports.default = pushToExplore; +exports.default = handleSubscribe; -},{"automerge":84}],19:[function(require,module,exports){ +},{"..":2,"automerge":100}],15:[function(require,module,exports){ "use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -/* eslint-disable no-param-reassign */ -const automerge_1 = __importDefault(require("automerge")); -async function pushToLiveFeed(key, match) { - if (!self.globalData.liveFeed[key]) { - // Create CRDT document with the new match added to it - try { - const origDoc = automerge_1.default.init(); - const newLiveFeed = automerge_1.default.change(origDoc, (doc) => { - doc[key] = new Date(match.internalMatch.date).getTime(); - }); - const currentDay = Math.floor(new Date().getTime() / (86400 * 1000)); - self.toolDb - .putCrdt(`matches-livefeed-${currentDay}`, automerge_1.default.getChanges(origDoc, newLiveFeed), false) - .catch(console.error); - // self.globalData.liveFeed = newLiveFeed; - } - catch (e) { - console.warn(e); - } +var _1 = require("."); +var getPeerSignature_1 = __importDefault(require("./utils/getPeerSignature")); +var ToolDbNetworkAdapter = /** @class */ (function () { + function ToolDbNetworkAdapter(db) { + var _this = this; + this._clientToSend = {}; + this._clientSocket = {}; + this._isClientConnected = {}; + this._clientIsServer = {}; + this._tooldb = db; + db.on("init", function () { + if (_this.tooldb.options.server) { + _this.getMeAsPeer().then(function (meAsPeer) { + _this.tooldb.peers[meAsPeer.pubkey] = meAsPeer; + }); + } + }); } -} -exports.default = pushToLiveFeed; - -},{"automerge":84}],20:[function(require,module,exports){ -"use strict"; -/* eslint-disable no-restricted-globals */ -Object.defineProperty(exports, "__esModule", { value: true }); -function queryKeys(msgId, key, userNamespaced, timeoutMs = 5000) { - return self.toolDb - .queryKeys(key, userNamespaced, timeoutMs) - .then((value) => { - self.postMessage({ type: `${msgId}_OK`, value }); - }) - .catch((err) => { - self.postMessage({ type: `${msgId}_ERR`, err }); + Object.defineProperty(ToolDbNetworkAdapter.prototype, "clientToSend", { + get: function () { + return this._clientToSend; + }, + enumerable: false, + configurable: true }); -} -exports.default = queryKeys; - -},{}],21:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -/* eslint-disable no-restricted-globals */ -function reduxAction(type, arg) { - self.postMessage({ type: "REDUX_ACTION", arg: { type, arg } }); -} -exports.default = reduxAction; - -},{}],22:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const constants_1 = require("./constants"); -/* eslint-disable no-restricted-globals */ -function removeHost(host) { - const networkModule = self.toolDb.network; - networkModule.disconnect(host); - // Try to conenct to servers from cache - self.toolDb.store.get(constants_1.SAVED_PEERS_KEY, (err, data) => { - let savedPeers = constants_1.DEFAULT_PEERS; - if (err) { - console.error("Error getting saved peers from cache:", err); + Object.defineProperty(ToolDbNetworkAdapter.prototype, "clientSocket", { + get: function () { + return this._clientSocket; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDbNetworkAdapter.prototype, "isClientConnected", { + get: function () { + return this._isClientConnected; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDbNetworkAdapter.prototype, "tooldb", { + get: function () { + return this._tooldb; + }, + enumerable: false, + configurable: true + }); + ToolDbNetworkAdapter.prototype.getMeAsPeer = function () { + var _this = this; + var timestamp = new Date().getTime(); + if (this.tooldb.options.defaultKeys === undefined) + return Promise.reject(); + else + return (0, getPeerSignature_1.default)(this.tooldb.options.defaultKeys.privateKey, this.tooldb.options.topic, timestamp, this.tooldb.options.host, this.tooldb.options.port).then(function (signature) { + var me = { + topic: _this.tooldb.options.topic, + timestamp: timestamp, + host: _this.tooldb.options.host, + port: _this.tooldb.options.port, + pubkey: _this.tooldb.getPubKey(), + sig: signature, + isServer: _this.tooldb.options.server, + }; + return me; + }); + }; + /** + * Check if the specified client is connected or not + * @param clientId Client ID + * @returns boolean + */ + ToolDbNetworkAdapter.prototype.isConnected = function (clientId) { + return this._isClientConnected[clientId] + ? this._isClientConnected[clientId]() + : false; + }; + /** + * Check if the specified client is a server/relay + * @param clientId Client ID + * @returns boolean + */ + ToolDbNetworkAdapter.prototype.isServer = function (clientId) { + return this._clientIsServer[clientId] || false; + }; + ToolDbNetworkAdapter.prototype.craftPingMessage = function () { + var _this = this; + return this.getMeAsPeer().then(function (meAsPeer) { + return JSON.stringify({ + type: "ping", + clientId: _this.getClientAddress(), + to: [_this.getClientAddress()], + isServer: _this.tooldb.options.server, + id: (0, _1.textRandom)(10), + peer: meAsPeer, + }); + }); + }; + /** + * Execute the function to send a message to the specified client ID + * @param clientId Client ID + * @param message Message + */ + ToolDbNetworkAdapter.prototype.executeSendToClient = function (clientId, message) { + if (this._clientToSend[clientId]) { + this._clientToSend[clientId](message); } - else if (data) { - try { - const newPeers = JSON.parse(data); - savedPeers = newPeers; + }; + ToolDbNetworkAdapter.prototype.getClientAddress = function () { + // This is not a good idea to use on all adapters, so it should be replaced + // if its causing issues. The only reason we use the last 20 chars is to + // muse the same peer address as the webrtc adapter. + return this.tooldb.pubKey; + }; + ToolDbNetworkAdapter.prototype.onClientDisconnect = function (clientId) { + delete this._clientToSend[clientId]; + delete this._clientIsServer[clientId]; + delete this._isClientConnected[clientId]; + }; + /** + * Should be called as a message payload handler + * This function will take care of processing the messages and also making sure + * we use the correct response methods troughout the adapter. + * @param message message payload + * @param clientId Client ID (can be null for ping/pong) + * @param setClientId Callback to set the client id on the parent class + */ + ToolDbNetworkAdapter.prototype.onClientMessage = function (message, clientId, setClientId) { + // this.tooldb.logger("onClientMessage", clientId); + if (clientId && !this.tooldb.processedOutHashes[clientId]) { + this.tooldb.processedOutHashes[clientId] = []; + } + try { + var parsedMessage = JSON.parse(message); + // We assume the first messages to arrive will always be ping or pong. + // Only after that we can get the client id for this socket. + if (parsedMessage.type === "ping" || parsedMessage.type === "pong") { + var cid = parsedMessage.clientId; + setClientId(cid); + this._clientIsServer[cid] = parsedMessage.isServer; + this.tooldb.processedOutHashes[cid] = []; + this.tooldb.clientOnMessage(parsedMessage, cid); } - catch (_e) { - console.error("Error parsing saved peers from cache:", _e); + else if (clientId) { + this.tooldb.clientOnMessage(parsedMessage, clientId); } } - savedPeers = savedPeers.filter((peer) => peer !== host); - self.toolDb.store.put(constants_1.SAVED_PEERS_KEY, JSON.stringify(savedPeers), () => { - console.log("Saved peers to cache", savedPeers); + catch (e) { + this.tooldb.logger("Got message ERR", message); + this.tooldb.logger(e); + } + }; + /** + * Sends a message to all peers connected to us + * This function takes care of the message deduplication, making sure we dont send + * the same message twice to the same peer. + * @param msg message data + * @param crossServerOnly If this message should be send to server peers only + * @param isRelay if we should relay this message + */ + ToolDbNetworkAdapter.prototype.sendToAll = function (msg, crossServerOnly) { + var _this = this; + if (crossServerOnly === void 0) { crossServerOnly = false; } + var pubkey = this.getClientAddress(); + var to = pubkey ? (0, _1.uniq)(__spreadArray(__spreadArray([], msg.to, true), [pubkey], false)) : msg.to; + var finalMessage = JSON.stringify(__assign(__assign({}, msg), { to: to })); + var filteredConns = Object.keys(this.clientToSend) + .filter(function (id) { return !to.includes(id); }) + .filter(function (clientId) { return _this.isConnected(clientId); }); + filteredConns.forEach(function (clientId) { + if ((crossServerOnly && _this.isServer(clientId)) || !crossServerOnly) { + _this.tooldb.logger(to.map(function (k) { return k.slice(-20); }), "Sent out to (all):", clientId.slice(-20)); + if (msg.type === "put" || msg.type === "crdtPut") { + if (!_this.tooldb.processedOutHashes[clientId].includes(msg.h)) { + _this.executeSendToClient(clientId, finalMessage); + _this.tooldb.processedOutHashes[clientId].push(msg.h); + } + } + else { + _this.executeSendToClient(clientId, finalMessage); + } + } + // } else { + // this.tooldb.logger("Fitlered out;", clientId); + // } }); - }); -} -exports.default = removeHost; + }; + /** + * Sends a message to a single peer. + * This function also takes care of the message deduplication. + * @param clientId Peer/Client id we want to send to. + * @param msg message data + */ + ToolDbNetworkAdapter.prototype.sendToClientId = function (clientId, msg) { + var pubkey = this.getClientAddress(); + var to = pubkey ? (0, _1.uniq)(__spreadArray(__spreadArray([], msg.to, true), [pubkey], false)) : msg.to; + var finalMessage = JSON.stringify(__assign(__assign({}, msg), { to: to })); + this.tooldb.logger(to.map(function (k) { return k.slice(-20); }), "Sent out to (single):", clientId.slice(-20)); + if (msg.type === "put" || msg.type === "crdtPut") { + if (clientId && + !this.tooldb.processedOutHashes[clientId].includes(msg.h)) { + this.executeSendToClient(clientId, finalMessage); + this.tooldb.processedOutHashes[clientId].push(msg.h); + } + } + else { + this.executeSendToClient(clientId, finalMessage); + } + }; + return ToolDbNetworkAdapter; +}()); +exports.default = ToolDbNetworkAdapter; -},{"./constants":3}],23:[function(require,module,exports){ +},{".":2,"./utils/getPeerSignature":60}],16:[function(require,module,exports){ "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const login_1 = __importDefault(require("./login")); -function waitMs(ms) { - return new Promise((resolve) => { - setTimeout(() => resolve(true), ms); - }); -} -/** - * Sign up process - * @param username plaintext username - * @param password sha1 of the plaintext password - * @returns Promise - */ -function signup(username, password) { - return self.toolDb - .signUp(username, password) - .then((msg) => { - // console.log("Sent signup! now wait"); - return waitMs(3000).then(() => { - // console.log("wait finished, now login"); - return (0, login_1.default)(username, password).then((_keys) => { - // console.log("login ok!?"); - return self.toolDb - .putData("userids", {}, true) - .then((_put) => { - return msg; - }); - }); +var encodeKeyString_1 = __importDefault(require("./utils/crypto/encodeKeyString")); +var exportKey_1 = __importDefault(require("./utils/crypto/exportKey")); +var generateKeysComb_1 = __importDefault(require("./utils/crypto/generateKeysComb")); +var randomAnimal_1 = __importDefault(require("./utils/randomAnimal")); +function toolDbAnonSignIn() { + var _this = this; + return (0, generateKeysComb_1.default)().then(function (newKeys) { + return (0, exportKey_1.default)("spki", newKeys.signKeys.publicKey) + .then(function (skpub) { return (0, encodeKeyString_1.default)(skpub); }) + .then(function (pubKey) { + _this.user = { + keys: newKeys, + name: "Anonymous " + (0, randomAnimal_1.default)(), + pubKey: pubKey, + }; + return newKeys; }); - }) - .catch((err) => { - self.postMessage({ type: "SIGNUP_ERR", err }); }); } -exports.default = signup; +exports.default = toolDbAnonSignIn; -},{"./login":17}],24:[function(require,module,exports){ +},{"./utils/crypto/encodeKeyString":47,"./utils/crypto/exportKey":50,"./utils/crypto/generateKeysComb":53,"./utils/randomAnimal":65}],17:[function(require,module,exports){ "use strict"; -/* eslint-disable no-restricted-globals */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); -const mtgatool_db_1 = require("mtgatool-db"); -const addHost_1 = __importDefault(require("./addHost")); -const constants_1 = require("./constants"); -const doFunction_1 = __importDefault(require("./doFunction")); -const exploreAggregation_1 = require("./exploreAggregation"); -const getConnectionData_1 = __importDefault(require("./getConnectionData")); -const getCrdt_1 = __importDefault(require("./getCrdt")); -const getData_1 = __importDefault(require("./getData")); -const getDataLocal_1 = __importDefault(require("./getDataLocal")); -const getMatchesData_1 = require("./getMatchesData"); -const getSaveKeysJson_1 = __importDefault(require("./getSaveKeysJson")); -const handleMatchesIndex_1 = __importDefault(require("./handleMatchesIndex")); -const keysLogin_1 = __importDefault(require("./keysLogin")); -const login_1 = __importDefault(require("./login")); -const pushToExplore_1 = __importDefault(require("./pushToExplore")); -const pushToLivefeed_1 = __importDefault(require("./pushToLivefeed")); -const queryKeys_1 = __importDefault(require("./queryKeys")); -const removeHost_1 = __importDefault(require("./removeHost")); -const signup_1 = __importDefault(require("./signup")); -const toolDb = new mtgatool_db_1.ToolDb({ - topic: "mtgatool-db-swarm-v4", - // debug: true, - server: false, - maxRetries: 999, -}); -toolDb.on("init", (key) => console.warn("ToolDb initialized!", key)); -// Try to conenct to servers from cache -toolDb.store.get(constants_1.SAVED_PEERS_KEY, (err, savedData) => { - let savedPeers = constants_1.DEFAULT_PEERS; - if (err) { - toolDb.store.put(constants_1.SAVED_PEERS_KEY, JSON.stringify(constants_1.DEFAULT_PEERS), () => { - console.log("Saved default peers to cache"); - }); +function toolDbClientOnMessage(message, remotePeerId) { + if (!this.processedIds[message.type]) { + this.processedIds[message.type] = []; } - else if (savedData) { - try { - const newPeers = JSON.parse(savedData); - savedPeers = newPeers; - } - catch (_e) { - console.error("Error parsing saved peers from cache:", _e); - } + if (this.processedIds[message.type].includes(message.id)) { + // console.warn( + // `Already processed this message > ${message.type} from ${remotePeerId}` + // ); + return; } - toolDb.store.get(constants_1.SERVERS_KEY, (serr, data) => { - let serversData = {}; - if (serr) { - console.error("Error getting servers from cache:", serr); - } - else if (data) { - try { - serversData = JSON.parse(data); - } - catch (_e) { - console.error("Error parsing servers from cache:", _e); - } + this.processedIds[message.type].push(message.id); + // console.warn(`Got message ${message.type} from ${remotePeerId}`); + this.logger("message", message, remotePeerId); + // Check if we are listening for this ID + if (message.id) { + var msgId = message.id; + if (this._idListeners[msgId]) { + this._idListeners[msgId](message); + this.removeIdListener(msgId); } - console.log("Got servers from cache:", serversData); - savedPeers.forEach((peer) => { - const networkModule = toolDb.network; - if (serversData[peer]) { - networkModule.connectTo(serversData[peer]); - } - else { - networkModule.findServer(peer); - } - }); - }); -}); -toolDb.onConnect = () => { - const networkModule = toolDb.network; - console.warn("ToolDb connected!"); - self.postMessage({ type: "CONNECTED" }); - toolDb.store.put(constants_1.SERVERS_KEY, JSON.stringify(networkModule.serverPeerData), () => console.log("Saved servers to cache", networkModule.serverPeerData)); -}; -self.toolDb = toolDb; -self.globalData = { - hiddenDecks: [], - liveFeed: {}, - fetchedAvatars: [], - matchesIndex: [], - draftsIndex: [], - currentUUID: "", -}; -self.onmessage = (e) => { - const { type } = e.data; - // console.log("Worker onmessage:", e.type, e.data); - switch (type) { - case "LOGIN": - (0, login_1.default)(e.data.username, e.data.password); - break; - case "KEYS_LOGIN": - (0, keysLogin_1.default)(e.data.username, e.data.keys); - break; - case "SIGNUP": - (0, signup_1.default)(e.data.username, e.data.password); - break; - case "PUT_DATA": - self.toolDb.putData(e.data.key, e.data.data, e.data.userNamespaced); - break; - case "GET_DATA": - (0, getData_1.default)(e.data.id, e.data.key, e.data.userNamespaced, e.data.timeoutMs); - break; - case "GET_CRDT": - (0, getCrdt_1.default)(e.data.id, e.data.key, e.data.userNamespaced, e.data.timeoutMs); - break; - case "GET_LOCAL_DATA": - (0, getDataLocal_1.default)(e.data.id, e.data.key); - break; - case "QUERY_KEYS": - (0, queryKeys_1.default)(e.data.id, e.data.key, e.data.userNamespaced, e.data.timeoutMs); - break; - case "DO_FUNCTION": - (0, doFunction_1.default)(e.data.id, e.data.fname, e.data.args); - break; - case "GET_CONNECTION_DATA": - (0, getConnectionData_1.default)(); + } + if (message === undefined || message.type === undefined) { + this.logger("Message is invalid!", message, typeof message); + return; + } + switch (message.type) { + case "ping": + this.handlePing(message, remotePeerId); break; - case "CONNECT": - (0, addHost_1.default)(e.data.peer.pubKey); - self.toolDb.network.connectTo(e.data.peer); + case "pong": + this.handlePong(message, remotePeerId); break; - case "DISCONNECT": - self.toolDb.network.disconnect(e.data.host); + case "subscribe": + this.handleSubscribe(message, remotePeerId); break; - case "REMOVE_HOST": - (0, removeHost_1.default)(e.data.host); + case "get": + this.handleGet(message, remotePeerId); break; - case "FIND_SERVER": - (0, addHost_1.default)(e.data.host); - self.toolDb.network.findServer(e.data.host); + case "put": + this.handlePut(message, remotePeerId); break; - case "GET_SAVE_KEYS_JSON": - (0, getSaveKeysJson_1.default)(); + case "crdtPut": + this.handleCrdtPut(message, remotePeerId); break; - // application specific handlers - case "PUSH_DB_MATCH": - (0, pushToExplore_1.default)(e.data.key, e.data.match); - (0, pushToLivefeed_1.default)(e.data.key, e.data.match); + case "crdtGet": + this.handleCrdtGet(message, remotePeerId); break; - case "EXPLORE_DATA_QUERY": - (0, exploreAggregation_1.beginDataQuery)(e.data.days, e.data.event); + case "query": + this.handleQuery(message, remotePeerId); break; - case "GET_MATCHES_DATA": - (0, getMatchesData_1.getMatchesData)(e.data.id, e.data.matchesIndex, e.data.uuid); + case "crdt": + this.handleCrdt(message, remotePeerId); break; - case "REFRESH_MATCHES": - if (self.toolDb.user) { - self.toolDb - .queryKeys(`:${self.toolDb.user.pubKey}.matches-`, false, 5000, true) - .then(handleMatchesIndex_1.default); - } + case "function": + this.handleFunction(message, remotePeerId); break; default: break; } -}; - -},{"./addHost":1,"./constants":3,"./doFunction":4,"./exploreAggregation":5,"./getConnectionData":6,"./getCrdt":7,"./getData":8,"./getDataLocal":9,"./getMatchesData":10,"./getSaveKeysJson":12,"./handleMatchesIndex":15,"./keysLogin":16,"./login":17,"./pushToExplore":18,"./pushToLivefeed":19,"./queryKeys":20,"./removeHost":22,"./signup":23,"mtgatool-db":291}],25:[function(require,module,exports){ -'use strict' - -const emptyOptions = Object.freeze({}) - -function AbstractChainedBatch (db) { - if (typeof db !== 'object' || db === null) { - throw new TypeError('First argument must be an abstract-leveldown compliant store') - } - - this.db = db - this._operations = [] - this._written = false } +exports.default = toolDbClientOnMessage; -AbstractChainedBatch.prototype._checkWritten = function () { - if (this._written) { - throw new Error('write() already called on this batch') - } +},{}],18:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var _1 = require("."); +/** + * Triggers a GET request to other peers. If the data is available locally it will return that instead. + * @param key key of the data + * @param userNamespaced If this key bolongs to a user or its public. Making it private will enforce validation for our public key and signatures. + * @param timeout Max time to wait for remote. + * @returns Promise + */ +function toolDbCrdtGet(key, userNamespaced, timeoutMs) { + var _this = this; + if (userNamespaced === void 0) { userNamespaced = false; } + if (timeoutMs === void 0) { timeoutMs = 1000; } + return new Promise(function (resolve, reject) { + var _a, _b; + if (userNamespaced && ((_a = _this.user) === null || _a === void 0 ? void 0 : _a.pubKey) === undefined) { + reject(new Error("You are not authorized yet!")); + return; + } + var finalKey = userNamespaced ? ":" + ((_b = _this.user) === null || _b === void 0 ? void 0 : _b.pubKey) + "." + key : key; + _this.logger("CRDT GET > " + finalKey); + var msgId = (0, _1.textRandom)(10); + var cancelTimeout = setTimeout(function () { + _this.loadCrdtDocument(finalKey).then(function (data) { + if (data) { + try { + _this.removeIdListener(msgId); + resolve(data); + } + catch (e) { + resolve(null); + } + } + else { + resolve(null); + } + }); + }, timeoutMs); + _this.addIdListener(msgId, function (msg) { + _this.logger("CRDT GET RECV > " + finalKey, msg); + clearTimeout(cancelTimeout); + if (msg.type === "crdt") { + resolve(msg.doc); + } + }); + // Do get + _this.network.sendToAll({ + type: "crdtGet", + to: [], + key: finalKey, + id: msgId, + }); + }); } +exports.default = toolDbCrdtGet; -AbstractChainedBatch.prototype.put = function (key, value, options) { - this._checkWritten() - - const err = this.db._checkKey(key) || this.db._checkValue(value) - if (err) throw err - - key = this.db._serializeKey(key) - value = this.db._serializeValue(value) - - this._put(key, value, options != null ? options : emptyOptions) - - return this +},{".":2}],19:[function(require,module,exports){ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var _1 = require("."); +var proofOfWork_1 = __importDefault(require("./utils/proofOfWork")); +var signData_1 = __importDefault(require("./utils/signData")); +/** + * Triggers a PUT request to other peers. + * @param key key where we want to put the data at. + * @param value Data we want to any (any type) + * @param userNamespaced If this key bolongs to a user or its public. Making it private will enforce validation for our public key and signatures. + * @returns Promise + */ +function toolDbCrdtPut(key, value, userNamespaced) { + var _this = this; + if (userNamespaced === void 0) { userNamespaced = false; } + return new Promise(function (resolve, reject) { + if (key.includes(".")) { + // Dots are used as a delimitator character between bublic keys and the key of the user's data + reject(new Error("Key cannot include dots!; " + key)); + return; + } + if (!_this.user) { + reject(new Error("You need to log in before you can PUT.")); + return; + } + var timestamp = new Date().getTime(); + var encodedData = JSON.stringify(value.map(_1.uint8ToBase64)); + var dataString = "" + encodedData + _this.user.pubKey + timestamp; + // WORK + (0, proofOfWork_1.default)(dataString, _this.options.pow) + .then(function (_a) { + var _b; + var hash = _a.hash, nonce = _a.nonce; + if ((_b = _this.user) === null || _b === void 0 ? void 0 : _b.keys) { + // Sign our value + (0, signData_1.default)(hash, _this.user.keys.signKeys.privateKey) + .then(function (signature) { return __awaiter(_this, void 0, void 0, function () { + var data, finalMessage; + var _a, _b; + return __generator(this, function (_c) { + data = { + k: userNamespaced ? ":" + ((_a = this.user) === null || _a === void 0 ? void 0 : _a.pubKey) + "." + key : key, + p: ((_b = this.user) === null || _b === void 0 ? void 0 : _b.pubKey) || "", + n: nonce, + t: timestamp, + h: hash, + s: (0, _1.arrayBufferToBase64)(signature), + v: encodedData, + }; + this.logger("PUT CRDT > " + key, data); + finalMessage = __assign({ type: "crdtPut", id: (0, _1.textRandom)(10), to: [] }, data); + this.network.sendToAll(finalMessage); + resolve(finalMessage); + return [2 /*return*/]; + }); + }); }) + .catch(reject); + } + }) + .catch(reject); + }); } +exports.default = toolDbCrdtPut; -AbstractChainedBatch.prototype._put = function (key, value, options) { - this._operations.push({ ...options, type: 'put', key, value }) +},{".":2,"./utils/proofOfWork":64,"./utils/signData":68}],20:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var _1 = require("."); +/** + * Triggers a FUNCTION request to other peers. If the function executes sucessfully it will return code "OK" + * @param function function name + * @param args arguments for the function + * @param timeout Max time to wait for remote. + * @returns Promise + */ +function toolDbFunction(fName, args, timeoutMs) { + var _this = this; + if (timeoutMs === void 0) { timeoutMs = 10000; } + return new Promise(function (resolve, reject) { + _this.logger("FUNCTION > " + fName); + var msgId = (0, _1.textRandom)(10); + var cancelTimeout = setTimeout(function () { + resolve({ return: "Timed out", code: "ERR" }); + }, timeoutMs); + _this.addIdListener(msgId, function (msg) { + _this.logger("FUNCTION RECV > " + fName, msg); + clearTimeout(cancelTimeout); + if (msg.type === "functionReturn") { + resolve({ return: msg.return, code: msg.code }); + } + }); + // Do get + _this.network.sendToAll({ + type: "function", + to: [], + function: fName, + args: args, + id: msgId, + }); + }); } +exports.default = toolDbFunction; -AbstractChainedBatch.prototype.del = function (key, options) { - this._checkWritten() - - const err = this.db._checkKey(key) - if (err) throw err - - key = this.db._serializeKey(key) - this._del(key, options != null ? options : emptyOptions) - - return this +},{".":2}],21:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var _1 = require("."); +/** + * Triggers a GET request to other peers. If the data is available locally it will return that instead. + * @param key key of the data + * @param userNamespaced If this key bolongs to a user or its public. Making it private will enforce validation for our public key and signatures. + * @param timeout Max time to wait for remote. + * @returns Promise + */ +function toolDbGet(key, userNamespaced, timeoutMs) { + var _this = this; + if (userNamespaced === void 0) { userNamespaced = false; } + if (timeoutMs === void 0) { timeoutMs = 1000; } + return new Promise(function (resolve, reject) { + var _a, _b; + if (userNamespaced && ((_a = _this.user) === null || _a === void 0 ? void 0 : _a.pubKey) === undefined) { + reject(new Error("You are not authorized yet!")); + return; + } + var finalKey = userNamespaced ? ":" + ((_b = _this.user) === null || _b === void 0 ? void 0 : _b.pubKey) + "." + key : key; + _this.logger("GET > " + finalKey); + var msgId = (0, _1.textRandom)(10); + var cancelTimeout = setTimeout(function () { + _this.store.get(finalKey, function (err, data) { + if (data) { + try { + var message = JSON.parse(data); + resolve(message.v); + } + catch (e) { + resolve(null); + } + } + else { + resolve(null); + } + }); + }, timeoutMs); + _this.addIdListener(msgId, function (msg) { + _this.logger("GET RECV > " + finalKey, msg); + clearTimeout(cancelTimeout); + if (msg.type === "put") { + resolve(msg.v); + } + }); + _this.store.get(finalKey, function (err, data) { + if (data) { + try { + var val = JSON.parse(data).v; + clearTimeout(cancelTimeout); + _this.removeIdListener(msgId); + resolve(val); + } + catch (e) { + // do nothing + } + } + }); + // Do get + _this.network.sendToAll({ + type: "get", + to: [], + key: finalKey, + id: msgId, + }); + }); } +exports.default = toolDbGet; -AbstractChainedBatch.prototype._del = function (key, options) { - this._operations.push({ ...options, type: 'del', key }) +},{".":2}],22:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function toolDbGetPubKey() { + if (this.pubKey === "" || this.pubKey === undefined) { + throw new Error("You are not authorized yet."); + } + return this.pubKey; } +exports.default = toolDbGetPubKey; -AbstractChainedBatch.prototype.clear = function () { - this._checkWritten() - this._clear() - - return this +},{}],23:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var encodeKeyString_1 = __importDefault(require("./utils/crypto/encodeKeyString")); +var exportKey_1 = __importDefault(require("./utils/crypto/exportKey")); +var randomAnimal_1 = __importDefault(require("./utils/randomAnimal")); +function toolDbKeysSignIn(keys, username) { + var _this = this; + return (0, exportKey_1.default)("spki", keys.signKeys.publicKey) + .then(function (skpub) { return (0, encodeKeyString_1.default)(skpub); }) + .then(function (pubKey) { + _this.user = { + keys: keys, + name: username || "Anonymous " + (0, randomAnimal_1.default)(), + pubKey: pubKey, + }; + return keys; + }); } +exports.default = toolDbKeysSignIn; -AbstractChainedBatch.prototype._clear = function () { - this._operations = [] -} - -AbstractChainedBatch.prototype.write = function (options, callback) { - this._checkWritten() - - if (typeof options === 'function') { - callback = options - } - if (typeof callback !== 'function') { - throw new Error('write() requires a callback argument') - } - if (typeof options !== 'object' || options === null) { - options = {} - } - - this._written = true - this._write(options, callback) -} - -AbstractChainedBatch.prototype._write = function (options, callback) { - this.db._batch(this._operations, options, callback) -} - -// Expose browser-compatible nextTick for dependents -AbstractChainedBatch.prototype._nextTick = require('./next-tick') - -module.exports = AbstractChainedBatch - -},{"./next-tick":30}],26:[function(require,module,exports){ -'use strict' - -function AbstractIterator (db) { - if (typeof db !== 'object' || db === null) { - throw new TypeError('First argument must be an abstract-leveldown compliant store') - } - - this.db = db - this._ended = false - this._nexting = false -} - -AbstractIterator.prototype.next = function (callback) { - // In callback mode, we return `this` - let ret = this - - if (callback === undefined) { - ret = new Promise(function (resolve, reject) { - callback = function (err, key, value) { - if (err) reject(err) - else if (key === undefined && value === undefined) resolve() - else resolve([key, value]) - } - }) - } else if (typeof callback !== 'function') { - throw new Error('next() requires a callback argument') - } - - if (this._ended) { - this._nextTick(callback, new Error('cannot call next() after end()')) - return ret - } - - if (this._nexting) { - this._nextTick(callback, new Error('cannot call next() before previous next() has completed')) - return ret - } - - this._nexting = true - this._next((err, ...rest) => { - this._nexting = false - callback(err, ...rest) - }) - - return ret -} - -AbstractIterator.prototype._next = function (callback) { - this._nextTick(callback) -} - -AbstractIterator.prototype.seek = function (target) { - if (this._ended) { - throw new Error('cannot call seek() after end()') - } - if (this._nexting) { - throw new Error('cannot call seek() before next() has completed') - } - - target = this.db._serializeKey(target) - this._seek(target) -} - -AbstractIterator.prototype._seek = function (target) {} - -AbstractIterator.prototype.end = function (callback) { - let promise - - if (callback === undefined) { - promise = new Promise(function (resolve, reject) { - callback = function (err) { - if (err) reject(err) - else resolve() - } - }) - } else if (typeof callback !== 'function') { - throw new Error('end() requires a callback argument') - } - - if (this._ended) { - this._nextTick(callback, new Error('end() already called on iterator')) - return promise - } - - this._ended = true - this._end(callback) - - return promise -} - -AbstractIterator.prototype._end = function (callback) { - this._nextTick(callback) -} - -AbstractIterator.prototype[Symbol.asyncIterator] = async function * () { - try { - let kv - - while ((kv = (await this.next())) !== undefined) { - yield kv - } - } finally { - if (!this._ended) await this.end() - } -} - -// Expose browser-compatible nextTick for dependents -AbstractIterator.prototype._nextTick = require('./next-tick') - -module.exports = AbstractIterator - -},{"./next-tick":30}],27:[function(require,module,exports){ -'use strict' - -const supports = require('level-supports') -const isBuffer = require('is-buffer') -const catering = require('catering') -const AbstractIterator = require('./abstract-iterator') -const AbstractChainedBatch = require('./abstract-chained-batch') -const getCallback = require('./lib/common').getCallback -const getOptions = require('./lib/common').getOptions - -const hasOwnProperty = Object.prototype.hasOwnProperty -const rangeOptions = ['lt', 'lte', 'gt', 'gte'] - -function AbstractLevelDOWN (manifest) { - this.status = 'new' - - // TODO (next major): make this mandatory - this.supports = supports(manifest, { - status: true - }) -} - -AbstractLevelDOWN.prototype.open = function (options, callback) { - const oldStatus = this.status - - if (typeof options === 'function') callback = options - - if (typeof callback !== 'function') { - throw new Error('open() requires a callback argument') - } - - if (typeof options !== 'object' || options === null) options = {} - - options.createIfMissing = options.createIfMissing !== false - options.errorIfExists = !!options.errorIfExists - - this.status = 'opening' - this._open(options, (err) => { - if (err) { - this.status = oldStatus - return callback(err) - } - this.status = 'open' - callback() - }) -} - -AbstractLevelDOWN.prototype._open = function (options, callback) { - this._nextTick(callback) -} - -AbstractLevelDOWN.prototype.close = function (callback) { - const oldStatus = this.status - - if (typeof callback !== 'function') { - throw new Error('close() requires a callback argument') - } - - this.status = 'closing' - this._close((err) => { - if (err) { - this.status = oldStatus - return callback(err) - } - this.status = 'closed' - callback() - }) -} - -AbstractLevelDOWN.prototype._close = function (callback) { - this._nextTick(callback) -} - -AbstractLevelDOWN.prototype.get = function (key, options, callback) { - if (typeof options === 'function') callback = options - - if (typeof callback !== 'function') { - throw new Error('get() requires a callback argument') - } - - const err = this._checkKey(key) - if (err) return this._nextTick(callback, err) - - key = this._serializeKey(key) - - if (typeof options !== 'object' || options === null) options = {} - - options.asBuffer = options.asBuffer !== false - - this._get(key, options, callback) -} - -AbstractLevelDOWN.prototype._get = function (key, options, callback) { - this._nextTick(function () { callback(new Error('NotFound')) }) -} - -AbstractLevelDOWN.prototype.getMany = function (keys, options, callback) { - callback = getCallback(options, callback) - callback = catering.fromCallback(callback) - options = getOptions(options) - - if (maybeError(this, callback)) { - return callback.promise - } - - if (!Array.isArray(keys)) { - this._nextTick(callback, new Error('getMany() requires an array argument')) - return callback.promise - } - - if (keys.length === 0) { - this._nextTick(callback, null, []) - return callback.promise - } - - if (typeof options.asBuffer !== 'boolean') { - options = { ...options, asBuffer: true } - } - - const serialized = new Array(keys.length) - - for (let i = 0; i < keys.length; i++) { - const key = keys[i] - const err = this._checkKey(key) - - if (err) { - this._nextTick(callback, err) - return callback.promise - } - - serialized[i] = this._serializeKey(key) - } - - this._getMany(serialized, options, callback) - return callback.promise -} - -AbstractLevelDOWN.prototype._getMany = function (keys, options, callback) { - this._nextTick(callback, null, new Array(keys.length).fill(undefined)) -} - -AbstractLevelDOWN.prototype.put = function (key, value, options, callback) { - if (typeof options === 'function') callback = options - - if (typeof callback !== 'function') { - throw new Error('put() requires a callback argument') - } - - const err = this._checkKey(key) || this._checkValue(value) - if (err) return this._nextTick(callback, err) - - key = this._serializeKey(key) - value = this._serializeValue(value) - - if (typeof options !== 'object' || options === null) options = {} - - this._put(key, value, options, callback) -} - -AbstractLevelDOWN.prototype._put = function (key, value, options, callback) { - this._nextTick(callback) -} - -AbstractLevelDOWN.prototype.del = function (key, options, callback) { - if (typeof options === 'function') callback = options - - if (typeof callback !== 'function') { - throw new Error('del() requires a callback argument') - } - - const err = this._checkKey(key) - if (err) return this._nextTick(callback, err) - - key = this._serializeKey(key) - - if (typeof options !== 'object' || options === null) options = {} - - this._del(key, options, callback) -} - -AbstractLevelDOWN.prototype._del = function (key, options, callback) { - this._nextTick(callback) -} - -AbstractLevelDOWN.prototype.batch = function (array, options, callback) { - if (!arguments.length) return this._chainedBatch() - - if (typeof options === 'function') callback = options - - if (typeof array === 'function') callback = array - - if (typeof callback !== 'function') { - throw new Error('batch(array) requires a callback argument') - } - - if (!Array.isArray(array)) { - return this._nextTick(callback, new Error('batch(array) requires an array argument')) - } - - if (array.length === 0) { - return this._nextTick(callback) - } - - if (typeof options !== 'object' || options === null) options = {} - - const serialized = new Array(array.length) - - for (let i = 0; i < array.length; i++) { - if (typeof array[i] !== 'object' || array[i] === null) { - return this._nextTick(callback, new Error('batch(array) element must be an object and not `null`')) - } - - const e = Object.assign({}, array[i]) - - if (e.type !== 'put' && e.type !== 'del') { - return this._nextTick(callback, new Error("`type` must be 'put' or 'del'")) - } - - const err = this._checkKey(e.key) - if (err) return this._nextTick(callback, err) - - e.key = this._serializeKey(e.key) - - if (e.type === 'put') { - const valueErr = this._checkValue(e.value) - if (valueErr) return this._nextTick(callback, valueErr) - - e.value = this._serializeValue(e.value) - } - - serialized[i] = e - } - - this._batch(serialized, options, callback) -} - -AbstractLevelDOWN.prototype._batch = function (array, options, callback) { - this._nextTick(callback) -} - -AbstractLevelDOWN.prototype.clear = function (options, callback) { - if (typeof options === 'function') { - callback = options - } else if (typeof callback !== 'function') { - throw new Error('clear() requires a callback argument') - } - - options = cleanRangeOptions(this, options) - options.reverse = !!options.reverse - options.limit = 'limit' in options ? options.limit : -1 - - this._clear(options, callback) -} - -AbstractLevelDOWN.prototype._clear = function (options, callback) { - // Avoid setupIteratorOptions, would serialize range options a second time. - options.keys = true - options.values = false - options.keyAsBuffer = true - options.valueAsBuffer = true - - const iterator = this._iterator(options) - const emptyOptions = {} - - const next = (err) => { - if (err) { - return iterator.end(function () { - callback(err) - }) - } - - iterator.next((err, key) => { - if (err) return next(err) - if (key === undefined) return iterator.end(callback) - - // This could be optimized by using a batch, but the default _clear - // is not meant to be fast. Implementations have more room to optimize - // if they override _clear. Note: using _del bypasses key serialization. - this._del(key, emptyOptions, next) - }) - } - - next() -} - -AbstractLevelDOWN.prototype._setupIteratorOptions = function (options) { - options = cleanRangeOptions(this, options) - - options.reverse = !!options.reverse - options.keys = options.keys !== false - options.values = options.values !== false - options.limit = 'limit' in options ? options.limit : -1 - options.keyAsBuffer = options.keyAsBuffer !== false - options.valueAsBuffer = options.valueAsBuffer !== false - - return options -} - -function cleanRangeOptions (db, options) { - const result = {} - - for (const k in options) { - if (!hasOwnProperty.call(options, k)) continue - - if (k === 'start' || k === 'end') { - throw new Error('Legacy range options ("start" and "end") have been removed') +},{"./utils/crypto/encodeKeyString":47,"./utils/crypto/exportKey":50,"./utils/randomAnimal":65}],24:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } - - let opt = options[k] - - if (isRangeOption(k)) { - // Note that we don't reject nullish and empty options here. While - // those types are invalid as keys, they are valid as range options. - opt = db._serializeKey(opt) - } - - result[k] = opt - } - - return result -} - -function isRangeOption (k) { - return rangeOptions.indexOf(k) !== -1 -} - -AbstractLevelDOWN.prototype.iterator = function (options) { - if (typeof options !== 'object' || options === null) options = {} - options = this._setupIteratorOptions(options) - return this._iterator(options) -} - -AbstractLevelDOWN.prototype._iterator = function (options) { - return new AbstractIterator(this) -} - -AbstractLevelDOWN.prototype._chainedBatch = function () { - return new AbstractChainedBatch(this) -} - -AbstractLevelDOWN.prototype._serializeKey = function (key) { - return key -} - -AbstractLevelDOWN.prototype._serializeValue = function (value) { - return value -} - -AbstractLevelDOWN.prototype._checkKey = function (key) { - if (key === null || key === undefined) { - return new Error('key cannot be `null` or `undefined`') - } else if (isBuffer(key) && key.length === 0) { // TODO: replace with typed array check - return new Error('key cannot be an empty Buffer') - } else if (key === '') { - return new Error('key cannot be an empty String') - } else if (Array.isArray(key) && key.length === 0) { - return new Error('key cannot be an empty Array') - } -} - -AbstractLevelDOWN.prototype._checkValue = function (value) { - if (value === null || value === undefined) { - return new Error('value cannot be `null` or `undefined`') - } -} - -// TODO: docs and tests -AbstractLevelDOWN.prototype.isOperational = function () { - return this.status === 'open' || this._isOperational() -} - -// Implementation may accept operations in other states too -AbstractLevelDOWN.prototype._isOperational = function () { - return false -} - -// Expose browser-compatible nextTick for dependents -// TODO: rename _nextTick to _queueMicrotask -// TODO: after we drop node 10, also use queueMicrotask in node -AbstractLevelDOWN.prototype._nextTick = require('./next-tick') - -module.exports = AbstractLevelDOWN - -function maybeError (db, callback) { - if (!db.isOperational()) { - db._nextTick(callback, new Error('Database is not open')) - return true - } - - return false -} - -},{"./abstract-chained-batch":25,"./abstract-iterator":26,"./lib/common":29,"./next-tick":30,"catering":157,"is-buffer":246,"level-supports":277}],28:[function(require,module,exports){ -'use strict' - -exports.AbstractLevelDOWN = require('./abstract-leveldown') -exports.AbstractIterator = require('./abstract-iterator') -exports.AbstractChainedBatch = require('./abstract-chained-batch') - -},{"./abstract-chained-batch":25,"./abstract-iterator":26,"./abstract-leveldown":27}],29:[function(require,module,exports){ -'use strict' - -exports.getCallback = function (options, callback) { - return typeof options === 'function' ? options : callback -} - -exports.getOptions = function (options) { - return typeof options === 'object' && options !== null ? options : {} -} - -},{}],30:[function(require,module,exports){ -'use strict' - -const queueMicrotask = require('queue-microtask') - -module.exports = function (fn, ...args) { - if (args.length === 0) { - queueMicrotask(fn) - } else { - queueMicrotask(() => fn(...args)) - } -} - -},{"queue-microtask":385}],31:[function(require,module,exports){ -'use strict'; - -const asn1 = exports; - -asn1.bignum = require('bn.js'); - -asn1.define = require('./asn1/api').define; -asn1.base = require('./asn1/base'); -asn1.constants = require('./asn1/constants'); -asn1.decoders = require('./asn1/decoders'); -asn1.encoders = require('./asn1/encoders'); - -},{"./asn1/api":32,"./asn1/base":34,"./asn1/constants":38,"./asn1/decoders":40,"./asn1/encoders":43,"bn.js":45}],32:[function(require,module,exports){ -'use strict'; - -const encoders = require('./encoders'); -const decoders = require('./decoders'); -const inherits = require('inherits'); - -const api = exports; - -api.define = function define(name, body) { - return new Entity(name, body); -}; - -function Entity(name, body) { - this.name = name; - this.body = body; - - this.decoders = {}; - this.encoders = {}; -} - -Entity.prototype._createNamed = function createNamed(Base) { - const name = this.name; - - function Generated(entity) { - this._initNamed(entity, name); - } - inherits(Generated, Base); - Generated.prototype._initNamed = function _initNamed(entity, name) { - Base.call(this, entity, name); - }; - - return new Generated(this); -}; - -Entity.prototype._getDecoder = function _getDecoder(enc) { - enc = enc || 'der'; - // Lazily create decoder - if (!this.decoders.hasOwnProperty(enc)) - this.decoders[enc] = this._createNamed(decoders[enc]); - return this.decoders[enc]; -}; - -Entity.prototype.decode = function decode(data, enc, options) { - return this._getDecoder(enc).decode(data, options); -}; - -Entity.prototype._getEncoder = function _getEncoder(enc) { - enc = enc || 'der'; - // Lazily create encoder - if (!this.encoders.hasOwnProperty(enc)) - this.encoders[enc] = this._createNamed(encoders[enc]); - return this.encoders[enc]; -}; - -Entity.prototype.encode = function encode(data, enc, /* internal */ reporter) { - return this._getEncoder(enc).encode(data, reporter); -}; - -},{"./decoders":40,"./encoders":43,"inherits":243}],33:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); -const Reporter = require('../base/reporter').Reporter; -const Buffer = require('safer-buffer').Buffer; - -function DecoderBuffer(base, options) { - Reporter.call(this, options); - if (!Buffer.isBuffer(base)) { - this.error('Input not Buffer'); - return; - } - - this.base = base; - this.offset = 0; - this.length = base.length; -} -inherits(DecoderBuffer, Reporter); -exports.DecoderBuffer = DecoderBuffer; - -DecoderBuffer.isDecoderBuffer = function isDecoderBuffer(data) { - if (data instanceof DecoderBuffer) { - return true; - } - - // Or accept compatible API - const isCompatible = typeof data === 'object' && - Buffer.isBuffer(data.base) && - data.constructor.name === 'DecoderBuffer' && - typeof data.offset === 'number' && - typeof data.length === 'number' && - typeof data.save === 'function' && - typeof data.restore === 'function' && - typeof data.isEmpty === 'function' && - typeof data.readUInt8 === 'function' && - typeof data.skip === 'function' && - typeof data.raw === 'function'; - - return isCompatible; -}; - -DecoderBuffer.prototype.save = function save() { - return { offset: this.offset, reporter: Reporter.prototype.save.call(this) }; -}; - -DecoderBuffer.prototype.restore = function restore(save) { - // Return skipped data - const res = new DecoderBuffer(this.base); - res.offset = save.offset; - res.length = this.offset; - - this.offset = save.offset; - Reporter.prototype.restore.call(this, save.reporter); - - return res; -}; - -DecoderBuffer.prototype.isEmpty = function isEmpty() { - return this.offset === this.length; -}; - -DecoderBuffer.prototype.readUInt8 = function readUInt8(fail) { - if (this.offset + 1 <= this.length) - return this.base.readUInt8(this.offset++, true); - else - return this.error(fail || 'DecoderBuffer overrun'); -}; - -DecoderBuffer.prototype.skip = function skip(bytes, fail) { - if (!(this.offset + bytes <= this.length)) - return this.error(fail || 'DecoderBuffer overrun'); - - const res = new DecoderBuffer(this.base); - - // Share reporter state - res._reporterState = this._reporterState; - - res.offset = this.offset; - res.length = this.offset + bytes; - this.offset += bytes; - return res; -}; - -DecoderBuffer.prototype.raw = function raw(save) { - return this.base.slice(save ? save.offset : this.offset, this.length); -}; - -function EncoderBuffer(value, reporter) { - if (Array.isArray(value)) { - this.length = 0; - this.value = value.map(function(item) { - if (!EncoderBuffer.isEncoderBuffer(item)) - item = new EncoderBuffer(item, reporter); - this.length += item.length; - return item; - }, this); - } else if (typeof value === 'number') { - if (!(0 <= value && value <= 0xff)) - return reporter.error('non-byte EncoderBuffer value'); - this.value = value; - this.length = 1; - } else if (typeof value === 'string') { - this.value = value; - this.length = Buffer.byteLength(value); - } else if (Buffer.isBuffer(value)) { - this.value = value; - this.length = value.length; - } else { - return reporter.error('Unsupported type: ' + typeof value); - } -} -exports.EncoderBuffer = EncoderBuffer; - -EncoderBuffer.isEncoderBuffer = function isEncoderBuffer(data) { - if (data instanceof EncoderBuffer) { - return true; - } - - // Or accept compatible API - const isCompatible = typeof data === 'object' && - data.constructor.name === 'EncoderBuffer' && - typeof data.length === 'number' && - typeof data.join === 'function'; - - return isCompatible; }; - -EncoderBuffer.prototype.join = function join(out, offset) { - if (!out) - out = Buffer.alloc(this.length); - if (!offset) - offset = 0; - - if (this.length === 0) - return out; - - if (Array.isArray(this.value)) { - this.value.forEach(function(item) { - item.join(out, offset); - offset += item.length; - }); - } else { - if (typeof this.value === 'number') - out[offset] = this.value; - else if (typeof this.value === 'string') - out.write(this.value, offset); - else if (Buffer.isBuffer(this.value)) - this.value.copy(out, offset); - offset += this.length; - } - - return out; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; - -},{"../base/reporter":36,"inherits":243,"safer-buffer":391}],34:[function(require,module,exports){ -'use strict'; - -const base = exports; - -base.Reporter = require('./reporter').Reporter; -base.DecoderBuffer = require('./buffer').DecoderBuffer; -base.EncoderBuffer = require('./buffer').EncoderBuffer; -base.Node = require('./node'); - -},{"./buffer":33,"./node":35,"./reporter":36}],35:[function(require,module,exports){ -'use strict'; - -const Reporter = require('../base/reporter').Reporter; -const EncoderBuffer = require('../base/buffer').EncoderBuffer; -const DecoderBuffer = require('../base/buffer').DecoderBuffer; -const assert = require('minimalistic-assert'); - -// Supported tags -const tags = [ - 'seq', 'seqof', 'set', 'setof', 'objid', 'bool', - 'gentime', 'utctime', 'null_', 'enum', 'int', 'objDesc', - 'bitstr', 'bmpstr', 'charstr', 'genstr', 'graphstr', 'ia5str', 'iso646str', - 'numstr', 'octstr', 'printstr', 't61str', 'unistr', 'utf8str', 'videostr' -]; - -// Public methods list -const methods = [ - 'key', 'obj', 'use', 'optional', 'explicit', 'implicit', 'def', 'choice', - 'any', 'contains' -].concat(tags); - -// Overrided methods list -const overrided = [ - '_peekTag', '_decodeTag', '_use', - '_decodeStr', '_decodeObjid', '_decodeTime', - '_decodeNull', '_decodeInt', '_decodeBool', '_decodeList', - - '_encodeComposite', '_encodeStr', '_encodeObjid', '_encodeTime', - '_encodeNull', '_encodeInt', '_encodeBool' -]; - -function Node(enc, parent, name) { - const state = {}; - this._baseState = state; - - state.name = name; - state.enc = enc; - - state.parent = parent || null; - state.children = null; - - // State - state.tag = null; - state.args = null; - state.reverseArgs = null; - state.choice = null; - state.optional = false; - state.any = false; - state.obj = false; - state.use = null; - state.useDecoder = null; - state.key = null; - state['default'] = null; - state.explicit = null; - state.implicit = null; - state.contains = null; - - // Should create new instance on each method - if (!state.parent) { - state.children = []; - this._wrap(); - } -} -module.exports = Node; - -const stateProps = [ - 'enc', 'parent', 'children', 'tag', 'args', 'reverseArgs', 'choice', - 'optional', 'any', 'obj', 'use', 'alteredUse', 'key', 'default', 'explicit', - 'implicit', 'contains' +Object.defineProperty(exports, "__esModule", { value: true }); +var ws_1 = __importDefault(require("ws")); +var _1 = require("."); +var arrayBufferToHex_1 = __importDefault(require("./utils/arrayBufferToHex")); +var waitFor_1 = __importDefault(require("./utils/waitFor")); +var defaultTrackerUrls = [ + "wss://tracker.webtorrent.dev", + "wss://tracker.openwebtorrent.com", + "wss://tracker.files.fm:7073/announce", + "wss://tooldb-tracker.herokuapp.com/", + //"wss://tracker.fastcast.nz/announce", + //"wss://tracker.btorrent.xyz/announce", + //"wss://tracker.webtorrent.io/announce", + //"wss://spacetradersapi-chatbox.herokuapp.com:443/announce", ]; - -Node.prototype.clone = function clone() { - const state = this._baseState; - const cstate = {}; - stateProps.forEach(function(prop) { - cstate[prop] = state[prop]; - }); - const res = new this.constructor(cstate.parent); - res._baseState = cstate; - return res; -}; - -Node.prototype._wrap = function wrap() { - const state = this._baseState; - methods.forEach(function(method) { - this[method] = function _wrappedMethod() { - const clone = new this.constructor(this); - state.children.push(clone); - return clone[method].apply(clone, arguments); - }; - }, this); -}; - -Node.prototype._init = function init(body) { - const state = this._baseState; - - assert(state.parent === null); - body.call(this); - - // Filter children - state.children = state.children.filter(function(child) { - return child._baseState.parent === this; - }, this); - assert.equal(state.children.length, 1, 'Root node can have only one child'); -}; - -Node.prototype._useArgs = function useArgs(args) { - const state = this._baseState; - - // Filter children and args - const children = args.filter(function(arg) { - return arg instanceof this.constructor; - }, this); - args = args.filter(function(arg) { - return !(arg instanceof this.constructor); - }, this); - - if (children.length !== 0) { - assert(state.children === null); - state.children = children; - - // Replace parent to maintain backward link - children.forEach(function(child) { - child._baseState.parent = this; - }, this); - } - if (args.length !== 0) { - assert(state.args === null); - state.args = args; - state.reverseArgs = args.map(function(arg) { - if (typeof arg !== 'object' || arg.constructor !== Object) - return arg; - - const res = {}; - Object.keys(arg).forEach(function(key) { - if (key == (key | 0)) - key |= 0; - const value = arg[key]; - res[value] = key; - }); - return res; - }); - } -}; - -// -// Overrided methods -// - -overrided.forEach(function(method) { - Node.prototype[method] = function _overrided() { - const state = this._baseState; - throw new Error(method + ' not implemented for encoding: ' + state.enc); - }; -}); - -// -// Public methods -// - -tags.forEach(function(tag) { - Node.prototype[tag] = function _tagMethod() { - const state = this._baseState; - const args = Array.prototype.slice.call(arguments); - - assert(state.tag === null); - state.tag = tag; - - this._useArgs(args); - - return this; - }; -}); - -Node.prototype.use = function use(item) { - assert(item); - const state = this._baseState; - - assert(state.use === null); - state.use = item; - - return this; -}; - -Node.prototype.optional = function optional() { - const state = this._baseState; - - state.optional = true; - - return this; -}; - -Node.prototype.def = function def(val) { - const state = this._baseState; - - assert(state['default'] === null); - state['default'] = val; - state.optional = true; - - return this; -}; - -Node.prototype.explicit = function explicit(num) { - const state = this._baseState; - - assert(state.explicit === null && state.implicit === null); - state.explicit = num; - - return this; -}; - -Node.prototype.implicit = function implicit(num) { - const state = this._baseState; - - assert(state.explicit === null && state.implicit === null); - state.implicit = num; - - return this; -}; - -Node.prototype.obj = function obj() { - const state = this._baseState; - const args = Array.prototype.slice.call(arguments); - - state.obj = true; - - if (args.length !== 0) - this._useArgs(args); - - return this; -}; - -Node.prototype.key = function key(newKey) { - const state = this._baseState; - - assert(state.key === null); - state.key = newKey; - - return this; -}; - -Node.prototype.any = function any() { - const state = this._baseState; - - state.any = true; - - return this; -}; - -Node.prototype.choice = function choice(obj) { - const state = this._baseState; - - assert(state.choice === null); - state.choice = obj; - this._useArgs(Object.keys(obj).map(function(key) { - return obj[key]; - })); - - return this; -}; - -Node.prototype.contains = function contains(item) { - const state = this._baseState; - - assert(state.use === null); - state.contains = item; - - return this; -}; - -// -// Decoding -// - -Node.prototype._decode = function decode(input, options) { - const state = this._baseState; - - // Decode root node - if (state.parent === null) - return input.wrapResult(state.children[0]._decode(input, options)); - - let result = state['default']; - let present = true; - - let prevKey = null; - if (state.key !== null) - prevKey = input.enterKey(state.key); - - // Check if tag is there - if (state.optional) { - let tag = null; - if (state.explicit !== null) - tag = state.explicit; - else if (state.implicit !== null) - tag = state.implicit; - else if (state.tag !== null) - tag = state.tag; - - if (tag === null && !state.any) { - // Trial and Error - const save = input.save(); - try { - if (state.choice === null) - this._decodeGeneric(state.tag, input, options); - else - this._decodeChoice(input, options); - present = true; - } catch (e) { - present = false; - } - input.restore(save); - } else { - present = this._peekTag(input, tag, state.any); - - if (input.isError(present)) - return present; - } - } - - // Push object on stack - let prevObj; - if (state.obj && present) - prevObj = input.enterObject(); - - if (present) { - // Unwrap explicit values - if (state.explicit !== null) { - const explicit = this._decodeTag(input, state.explicit); - if (input.isError(explicit)) - return explicit; - input = explicit; - } - - const start = input.offset; - - // Unwrap implicit and normal values - if (state.use === null && state.choice === null) { - let save; - if (state.any) - save = input.save(); - const body = this._decodeTag( - input, - state.implicit !== null ? state.implicit : state.tag, - state.any - ); - if (input.isError(body)) - return body; - - if (state.any) - result = input.raw(save); - else - input = body; - } - - if (options && options.track && state.tag !== null) - options.track(input.path(), start, input.length, 'tagged'); - - if (options && options.track && state.tag !== null) - options.track(input.path(), input.offset, input.length, 'content'); - - // Select proper method for tag - if (state.any) { - // no-op - } else if (state.choice === null) { - result = this._decodeGeneric(state.tag, input, options); - } else { - result = this._decodeChoice(input, options); - } - - if (input.isError(result)) - return result; - - // Decode children - if (!state.any && state.choice === null && state.children !== null) { - state.children.forEach(function decodeChildren(child) { - // NOTE: We are ignoring errors here, to let parser continue with other - // parts of encoded data - child._decode(input, options); - }); - } - - // Decode contained/encoded by schema, only in bit or octet strings - if (state.contains && (state.tag === 'octstr' || state.tag === 'bitstr')) { - const data = new DecoderBuffer(result); - result = this._getUse(state.contains, input._reporterState.obj) - ._decode(data, options); - } - } - - // Pop object - if (state.obj && present) - result = input.leaveObject(prevObj); - - // Set key - if (state.key !== null && (result !== null || present === true)) - input.leaveKey(prevKey, state.key, result); - else if (prevKey !== null) - input.exitKey(prevKey); - - return result; -}; - -Node.prototype._decodeGeneric = function decodeGeneric(tag, input, options) { - const state = this._baseState; - - if (tag === 'seq' || tag === 'set') - return null; - if (tag === 'seqof' || tag === 'setof') - return this._decodeList(input, tag, state.args[0], options); - else if (/str$/.test(tag)) - return this._decodeStr(input, tag, options); - else if (tag === 'objid' && state.args) - return this._decodeObjid(input, state.args[0], state.args[1], options); - else if (tag === 'objid') - return this._decodeObjid(input, null, null, options); - else if (tag === 'gentime' || tag === 'utctime') - return this._decodeTime(input, tag, options); - else if (tag === 'null_') - return this._decodeNull(input, options); - else if (tag === 'bool') - return this._decodeBool(input, options); - else if (tag === 'objDesc') - return this._decodeStr(input, tag, options); - else if (tag === 'int' || tag === 'enum') - return this._decodeInt(input, state.args && state.args[0], options); - - if (state.use !== null) { - return this._getUse(state.use, input._reporterState.obj) - ._decode(input, options); - } else { - return input.error('unknown tag: ' + tag); - } -}; - -Node.prototype._getUse = function _getUse(entity, obj) { - - const state = this._baseState; - // Create altered use decoder if implicit is set - state.useDecoder = this._use(entity, obj); - assert(state.useDecoder._baseState.parent === null); - state.useDecoder = state.useDecoder._baseState.children[0]; - if (state.implicit !== state.useDecoder._baseState.implicit) { - state.useDecoder = state.useDecoder.clone(); - state.useDecoder._baseState.implicit = state.implicit; - } - return state.useDecoder; -}; - -Node.prototype._decodeChoice = function decodeChoice(input, options) { - const state = this._baseState; - let result = null; - let match = false; - - Object.keys(state.choice).some(function(key) { - const save = input.save(); - const node = state.choice[key]; - try { - const value = node._decode(input, options); - if (input.isError(value)) - return false; - - result = { type: key, value: value }; - match = true; - } catch (e) { - input.restore(save); - return false; - } - return true; - }, this); - - if (!match) - return input.error('Choice not matched'); - - return result; -}; - -// -// Encoding -// - -Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) { - return new EncoderBuffer(data, this.reporter); -}; - -Node.prototype._encode = function encode(data, reporter, parent) { - const state = this._baseState; - if (state['default'] !== null && state['default'] === data) - return; - - const result = this._encodeValue(data, reporter, parent); - if (result === undefined) - return; - - if (this._skipDefault(result, reporter, parent)) - return; - - return result; -}; - -Node.prototype._encodeValue = function encode(data, reporter, parent) { - const state = this._baseState; - - // Decode root node - if (state.parent === null) - return state.children[0]._encode(data, reporter || new Reporter()); - - let result = null; - - // Set reporter to share it with a child class - this.reporter = reporter; - - // Check if data is there - if (state.optional && data === undefined) { - if (state['default'] !== null) - data = state['default']; - else - return; - } - - // Encode children first - let content = null; - let primitive = false; - if (state.any) { - // Anything that was given is translated to buffer - result = this._createEncoderBuffer(data); - } else if (state.choice) { - result = this._encodeChoice(data, reporter); - } else if (state.contains) { - content = this._getUse(state.contains, parent)._encode(data, reporter); - primitive = true; - } else if (state.children) { - content = state.children.map(function(child) { - if (child._baseState.tag === 'null_') - return child._encode(null, reporter, data); - - if (child._baseState.key === null) - return reporter.error('Child should have a key'); - const prevKey = reporter.enterKey(child._baseState.key); - - if (typeof data !== 'object') - return reporter.error('Child expected, but input is not object'); - - const res = child._encode(data[child._baseState.key], reporter, data); - reporter.leaveKey(prevKey); - - return res; - }, this).filter(function(child) { - return child; - }); - content = this._createEncoderBuffer(content); - } else { - if (state.tag === 'seqof' || state.tag === 'setof') { - // TODO(indutny): this should be thrown on DSL level - if (!(state.args && state.args.length === 1)) - return reporter.error('Too many args for : ' + state.tag); - - if (!Array.isArray(data)) - return reporter.error('seqof/setof, but data is not Array'); - - const child = this.clone(); - child._baseState.implicit = null; - content = this._createEncoderBuffer(data.map(function(item) { - const state = this._baseState; - - return this._getUse(state.args[0], data)._encode(item, reporter); - }, child)); - } else if (state.use !== null) { - result = this._getUse(state.use, parent)._encode(data, reporter); - } else { - content = this._encodePrimitive(state.tag, data); - primitive = true; - } - } - - // Encode data itself - if (!state.any && state.choice === null) { - const tag = state.implicit !== null ? state.implicit : state.tag; - const cls = state.implicit === null ? 'universal' : 'context'; - - if (tag === null) { - if (state.use === null) - reporter.error('Tag could be omitted only for .use()'); - } else { - if (state.use === null) - result = this._encodeComposite(tag, primitive, cls, content); - } - } - - // Wrap in explicit - if (state.explicit !== null) - result = this._encodeComposite(state.explicit, false, 'context', result); - - return result; -}; - -Node.prototype._encodeChoice = function encodeChoice(data, reporter) { - const state = this._baseState; - - const node = state.choice[data.type]; - if (!node) { - assert( - false, - data.type + ' not found in ' + - JSON.stringify(Object.keys(state.choice))); - } - return node._encode(data.value, reporter); -}; - -Node.prototype._encodePrimitive = function encodePrimitive(tag, data) { - const state = this._baseState; - - if (/str$/.test(tag)) - return this._encodeStr(data, tag); - else if (tag === 'objid' && state.args) - return this._encodeObjid(data, state.reverseArgs[0], state.args[1]); - else if (tag === 'objid') - return this._encodeObjid(data, null, null); - else if (tag === 'gentime' || tag === 'utctime') - return this._encodeTime(data, tag); - else if (tag === 'null_') - return this._encodeNull(); - else if (tag === 'int' || tag === 'enum') - return this._encodeInt(data, state.args && state.reverseArgs[0]); - else if (tag === 'bool') - return this._encodeBool(data); - else if (tag === 'objDesc') - return this._encodeStr(data, tag); - else - throw new Error('Unsupported tag: ' + tag); -}; - -Node.prototype._isNumstr = function isNumstr(str) { - return /^[0-9 ]*$/.test(str); -}; - -Node.prototype._isPrintstr = function isPrintstr(str) { - return /^[A-Za-z0-9 '()+,-./:=?]*$/.test(str); -}; - -},{"../base/buffer":33,"../base/reporter":36,"minimalistic-assert":288}],36:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); - -function Reporter(options) { - this._reporterState = { - obj: null, - path: [], - options: options || {}, - errors: [] - }; -} -exports.Reporter = Reporter; - -Reporter.prototype.isError = function isError(obj) { - return obj instanceof ReporterError; -}; - -Reporter.prototype.save = function save() { - const state = this._reporterState; - - return { obj: state.obj, pathLen: state.path.length }; -}; - -Reporter.prototype.restore = function restore(data) { - const state = this._reporterState; - - state.obj = data.obj; - state.path = state.path.slice(0, data.pathLen); -}; - -Reporter.prototype.enterKey = function enterKey(key) { - return this._reporterState.path.push(key); -}; - -Reporter.prototype.exitKey = function exitKey(index) { - const state = this._reporterState; - - state.path = state.path.slice(0, index - 1); -}; - -Reporter.prototype.leaveKey = function leaveKey(index, key, value) { - const state = this._reporterState; - - this.exitKey(index); - if (state.obj !== null) - state.obj[key] = value; -}; - -Reporter.prototype.path = function path() { - return this._reporterState.path.join('/'); -}; - -Reporter.prototype.enterObject = function enterObject() { - const state = this._reporterState; - - const prev = state.obj; - state.obj = {}; - return prev; -}; - -Reporter.prototype.leaveObject = function leaveObject(prev) { - const state = this._reporterState; - - const now = state.obj; - state.obj = prev; - return now; -}; - -Reporter.prototype.error = function error(msg) { - let err; - const state = this._reporterState; - - const inherited = msg instanceof ReporterError; - if (inherited) { - err = msg; - } else { - err = new ReporterError(state.path.map(function(elem) { - return '[' + JSON.stringify(elem) + ']'; - }).join(''), msg.message || msg, msg.stack); - } - - if (!state.options.partial) - throw err; - - if (!inherited) - state.errors.push(err); - - return err; -}; - -Reporter.prototype.wrapResult = function wrapResult(result) { - const state = this._reporterState; - if (!state.options.partial) - return result; - - return { - result: this.isError(result) ? null : result, - errors: state.errors - }; -}; - -function ReporterError(path, msg) { - this.path = path; - this.rethrow(msg); -} -inherits(ReporterError, Error); - -ReporterError.prototype.rethrow = function rethrow(msg) { - this.message = msg + ' at: ' + (this.path || '(shallow)'); - if (Error.captureStackTrace) - Error.captureStackTrace(this, ReporterError); - - if (!this.stack) { - try { - // IE only adds stack when thrown - throw new Error(this.message); - } catch (e) { - this.stack = e.stack; - } - } - return this; -}; - -},{"inherits":243}],37:[function(require,module,exports){ -'use strict'; - -// Helper -function reverse(map) { - const res = {}; - - Object.keys(map).forEach(function(key) { - // Convert key to integer if it is stringified - if ((key | 0) == key) - key = key | 0; - - const value = map[key]; - res[value] = key; - }); - - return res; -} - -exports.tagClass = { - 0: 'universal', - 1: 'application', - 2: 'context', - 3: 'private' -}; -exports.tagClassByName = reverse(exports.tagClass); - -exports.tag = { - 0x00: 'end', - 0x01: 'bool', - 0x02: 'int', - 0x03: 'bitstr', - 0x04: 'octstr', - 0x05: 'null_', - 0x06: 'objid', - 0x07: 'objDesc', - 0x08: 'external', - 0x09: 'real', - 0x0a: 'enum', - 0x0b: 'embed', - 0x0c: 'utf8str', - 0x0d: 'relativeOid', - 0x10: 'seq', - 0x11: 'set', - 0x12: 'numstr', - 0x13: 'printstr', - 0x14: 't61str', - 0x15: 'videostr', - 0x16: 'ia5str', - 0x17: 'utctime', - 0x18: 'gentime', - 0x19: 'graphstr', - 0x1a: 'iso646str', - 0x1b: 'genstr', - 0x1c: 'unistr', - 0x1d: 'charstr', - 0x1e: 'bmpstr' -}; -exports.tagByName = reverse(exports.tag); - -},{}],38:[function(require,module,exports){ -'use strict'; - -const constants = exports; - -// Helper -constants._reverse = function reverse(map) { - const res = {}; - - Object.keys(map).forEach(function(key) { - // Convert key to integer if it is stringified - if ((key | 0) == key) - key = key | 0; - - const value = map[key]; - res[value] = key; - }); - - return res; -}; - -constants.der = require('./der'); - -},{"./der":37}],39:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); - -const bignum = require('bn.js'); -const DecoderBuffer = require('../base/buffer').DecoderBuffer; -const Node = require('../base/node'); - -// Import DER constants -const der = require('../constants/der'); - -function DERDecoder(entity) { - this.enc = 'der'; - this.name = entity.name; - this.entity = entity; - - // Construct base tree - this.tree = new DERNode(); - this.tree._init(entity.body); -} -module.exports = DERDecoder; - -DERDecoder.prototype.decode = function decode(data, options) { - if (!DecoderBuffer.isDecoderBuffer(data)) { - data = new DecoderBuffer(data, options); - } - - return this.tree._decode(data, options); -}; - -// Tree methods - -function DERNode(parent) { - Node.call(this, 'der', parent); -} -inherits(DERNode, Node); - -DERNode.prototype._peekTag = function peekTag(buffer, tag, any) { - if (buffer.isEmpty()) - return false; - - const state = buffer.save(); - const decodedTag = derDecodeTag(buffer, 'Failed to peek tag: "' + tag + '"'); - if (buffer.isError(decodedTag)) - return decodedTag; - - buffer.restore(state); - - return decodedTag.tag === tag || decodedTag.tagStr === tag || - (decodedTag.tagStr + 'of') === tag || any; -}; - -DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) { - const decodedTag = derDecodeTag(buffer, - 'Failed to decode tag of "' + tag + '"'); - if (buffer.isError(decodedTag)) - return decodedTag; - - let len = derDecodeLen(buffer, - decodedTag.primitive, - 'Failed to get length of "' + tag + '"'); - - // Failure - if (buffer.isError(len)) - return len; - - if (!any && - decodedTag.tag !== tag && - decodedTag.tagStr !== tag && - decodedTag.tagStr + 'of' !== tag) { - return buffer.error('Failed to match tag: "' + tag + '"'); - } - - if (decodedTag.primitive || len !== null) - return buffer.skip(len, 'Failed to match body of: "' + tag + '"'); - - // Indefinite length... find END tag - const state = buffer.save(); - const res = this._skipUntilEnd( - buffer, - 'Failed to skip indefinite length body: "' + this.tag + '"'); - if (buffer.isError(res)) - return res; - - len = buffer.offset - state.offset; - buffer.restore(state); - return buffer.skip(len, 'Failed to match body of: "' + tag + '"'); -}; - -DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer, fail) { - for (;;) { - const tag = derDecodeTag(buffer, fail); - if (buffer.isError(tag)) - return tag; - const len = derDecodeLen(buffer, tag.primitive, fail); - if (buffer.isError(len)) - return len; - - let res; - if (tag.primitive || len !== null) - res = buffer.skip(len); - else - res = this._skipUntilEnd(buffer, fail); - - // Failure - if (buffer.isError(res)) - return res; - - if (tag.tagStr === 'end') - break; - } -}; - -DERNode.prototype._decodeList = function decodeList(buffer, tag, decoder, - options) { - const result = []; - while (!buffer.isEmpty()) { - const possibleEnd = this._peekTag(buffer, 'end'); - if (buffer.isError(possibleEnd)) - return possibleEnd; - - const res = decoder.decode(buffer, 'der', options); - if (buffer.isError(res) && possibleEnd) - break; - result.push(res); - } - return result; -}; - -DERNode.prototype._decodeStr = function decodeStr(buffer, tag) { - if (tag === 'bitstr') { - const unused = buffer.readUInt8(); - if (buffer.isError(unused)) - return unused; - return { unused: unused, data: buffer.raw() }; - } else if (tag === 'bmpstr') { - const raw = buffer.raw(); - if (raw.length % 2 === 1) - return buffer.error('Decoding of string type: bmpstr length mismatch'); - - let str = ''; - for (let i = 0; i < raw.length / 2; i++) { - str += String.fromCharCode(raw.readUInt16BE(i * 2)); - } - return str; - } else if (tag === 'numstr') { - const numstr = buffer.raw().toString('ascii'); - if (!this._isNumstr(numstr)) { - return buffer.error('Decoding of string type: ' + - 'numstr unsupported characters'); - } - return numstr; - } else if (tag === 'octstr') { - return buffer.raw(); - } else if (tag === 'objDesc') { - return buffer.raw(); - } else if (tag === 'printstr') { - const printstr = buffer.raw().toString('ascii'); - if (!this._isPrintstr(printstr)) { - return buffer.error('Decoding of string type: ' + - 'printstr unsupported characters'); - } - return printstr; - } else if (/str$/.test(tag)) { - return buffer.raw().toString(); - } else { - return buffer.error('Decoding of string type: ' + tag + ' unsupported'); - } -}; - -DERNode.prototype._decodeObjid = function decodeObjid(buffer, values, relative) { - let result; - const identifiers = []; - let ident = 0; - let subident = 0; - while (!buffer.isEmpty()) { - subident = buffer.readUInt8(); - ident <<= 7; - ident |= subident & 0x7f; - if ((subident & 0x80) === 0) { - identifiers.push(ident); - ident = 0; - } - } - if (subident & 0x80) - identifiers.push(ident); - - const first = (identifiers[0] / 40) | 0; - const second = identifiers[0] % 40; - - if (relative) - result = identifiers; - else - result = [first, second].concat(identifiers.slice(1)); - - if (values) { - let tmp = values[result.join(' ')]; - if (tmp === undefined) - tmp = values[result.join('.')]; - if (tmp !== undefined) - result = tmp; - } - - return result; -}; - -DERNode.prototype._decodeTime = function decodeTime(buffer, tag) { - const str = buffer.raw().toString(); - - let year; - let mon; - let day; - let hour; - let min; - let sec; - if (tag === 'gentime') { - year = str.slice(0, 4) | 0; - mon = str.slice(4, 6) | 0; - day = str.slice(6, 8) | 0; - hour = str.slice(8, 10) | 0; - min = str.slice(10, 12) | 0; - sec = str.slice(12, 14) | 0; - } else if (tag === 'utctime') { - year = str.slice(0, 2) | 0; - mon = str.slice(2, 4) | 0; - day = str.slice(4, 6) | 0; - hour = str.slice(6, 8) | 0; - min = str.slice(8, 10) | 0; - sec = str.slice(10, 12) | 0; - if (year < 70) - year = 2000 + year; - else - year = 1900 + year; - } else { - return buffer.error('Decoding ' + tag + ' time is not supported yet'); - } - - return Date.UTC(year, mon - 1, day, hour, min, sec, 0); -}; - -DERNode.prototype._decodeNull = function decodeNull() { - return null; -}; - -DERNode.prototype._decodeBool = function decodeBool(buffer) { - const res = buffer.readUInt8(); - if (buffer.isError(res)) - return res; - else - return res !== 0; -}; - -DERNode.prototype._decodeInt = function decodeInt(buffer, values) { - // Bigint, return as it is (assume big endian) - const raw = buffer.raw(); - let res = new bignum(raw); - - if (values) - res = values[res.toString(10)] || res; - - return res; -}; - -DERNode.prototype._use = function use(entity, obj) { - if (typeof entity === 'function') - entity = entity(obj); - return entity._getDecoder('der').tree; -}; - -// Utility methods - -function derDecodeTag(buf, fail) { - let tag = buf.readUInt8(fail); - if (buf.isError(tag)) - return tag; - - const cls = der.tagClass[tag >> 6]; - const primitive = (tag & 0x20) === 0; - - // Multi-octet tag - load - if ((tag & 0x1f) === 0x1f) { - let oct = tag; - tag = 0; - while ((oct & 0x80) === 0x80) { - oct = buf.readUInt8(fail); - if (buf.isError(oct)) - return oct; - - tag <<= 7; - tag |= oct & 0x7f; - } - } else { - tag &= 0x1f; - } - const tagStr = der.tag[tag]; - - return { - cls: cls, - primitive: primitive, - tag: tag, - tagStr: tagStr - }; -} - -function derDecodeLen(buf, primitive, fail) { - let len = buf.readUInt8(fail); - if (buf.isError(len)) - return len; - - // Indefinite form - if (!primitive && len === 0x80) - return null; - - // Definite form - if ((len & 0x80) === 0) { - // Short form - return len; - } - - // Long form - const num = len & 0x7f; - if (num > 4) - return buf.error('length octect is too long'); - - len = 0; - for (let i = 0; i < num; i++) { - len <<= 8; - const j = buf.readUInt8(fail); - if (buf.isError(j)) - return j; - len |= j; - } - - return len; -} - -},{"../base/buffer":33,"../base/node":35,"../constants/der":37,"bn.js":45,"inherits":243}],40:[function(require,module,exports){ -'use strict'; - -const decoders = exports; - -decoders.der = require('./der'); -decoders.pem = require('./pem'); - -},{"./der":39,"./pem":41}],41:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); -const Buffer = require('safer-buffer').Buffer; - -const DERDecoder = require('./der'); - -function PEMDecoder(entity) { - DERDecoder.call(this, entity); - this.enc = 'pem'; -} -inherits(PEMDecoder, DERDecoder); -module.exports = PEMDecoder; - -PEMDecoder.prototype.decode = function decode(data, options) { - const lines = data.toString().split(/[\r\n]+/g); - - const label = options.label.toUpperCase(); - - const re = /^-----(BEGIN|END) ([^-]+)-----$/; - let start = -1; - let end = -1; - for (let i = 0; i < lines.length; i++) { - const match = lines[i].match(re); - if (match === null) - continue; - - if (match[2] !== label) - continue; - - if (start === -1) { - if (match[1] !== 'BEGIN') - break; - start = i; - } else { - if (match[1] !== 'END') - break; - end = i; - break; - } - } - if (start === -1 || end === -1) - throw new Error('PEM section not found for: ' + label); - - const base64 = lines.slice(start + 1, end).join(''); - // Remove excessive symbols - base64.replace(/[^a-z0-9+/=]+/gi, ''); - - const input = Buffer.from(base64, 'base64'); - return DERDecoder.prototype.decode.call(this, input, options); -}; - -},{"./der":39,"inherits":243,"safer-buffer":391}],42:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); -const Buffer = require('safer-buffer').Buffer; -const Node = require('../base/node'); - -// Import DER constants -const der = require('../constants/der'); - -function DEREncoder(entity) { - this.enc = 'der'; - this.name = entity.name; - this.entity = entity; - - // Construct base tree - this.tree = new DERNode(); - this.tree._init(entity.body); -} -module.exports = DEREncoder; - -DEREncoder.prototype.encode = function encode(data, reporter) { - return this.tree._encode(data, reporter).join(); -}; - -// Tree methods - -function DERNode(parent) { - Node.call(this, 'der', parent); -} -inherits(DERNode, Node); - -DERNode.prototype._encodeComposite = function encodeComposite(tag, - primitive, - cls, - content) { - const encodedTag = encodeTag(tag, primitive, cls, this.reporter); - - // Short form - if (content.length < 0x80) { - const header = Buffer.alloc(2); - header[0] = encodedTag; - header[1] = content.length; - return this._createEncoderBuffer([ header, content ]); - } - - // Long form - // Count octets required to store length - let lenOctets = 1; - for (let i = content.length; i >= 0x100; i >>= 8) - lenOctets++; - - const header = Buffer.alloc(1 + 1 + lenOctets); - header[0] = encodedTag; - header[1] = 0x80 | lenOctets; - - for (let i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8) - header[i] = j & 0xff; - - return this._createEncoderBuffer([ header, content ]); -}; - -DERNode.prototype._encodeStr = function encodeStr(str, tag) { - if (tag === 'bitstr') { - return this._createEncoderBuffer([ str.unused | 0, str.data ]); - } else if (tag === 'bmpstr') { - const buf = Buffer.alloc(str.length * 2); - for (let i = 0; i < str.length; i++) { - buf.writeUInt16BE(str.charCodeAt(i), i * 2); - } - return this._createEncoderBuffer(buf); - } else if (tag === 'numstr') { - if (!this._isNumstr(str)) { - return this.reporter.error('Encoding of string type: numstr supports ' + - 'only digits and space'); - } - return this._createEncoderBuffer(str); - } else if (tag === 'printstr') { - if (!this._isPrintstr(str)) { - return this.reporter.error('Encoding of string type: printstr supports ' + - 'only latin upper and lower case letters, ' + - 'digits, space, apostrophe, left and rigth ' + - 'parenthesis, plus sign, comma, hyphen, ' + - 'dot, slash, colon, equal sign, ' + - 'question mark'); - } - return this._createEncoderBuffer(str); - } else if (/str$/.test(tag)) { - return this._createEncoderBuffer(str); - } else if (tag === 'objDesc') { - return this._createEncoderBuffer(str); - } else { - return this.reporter.error('Encoding of string type: ' + tag + - ' unsupported'); - } -}; - -DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) { - if (typeof id === 'string') { - if (!values) - return this.reporter.error('string objid given, but no values map found'); - if (!values.hasOwnProperty(id)) - return this.reporter.error('objid not found in values map'); - id = values[id].split(/[\s.]+/g); - for (let i = 0; i < id.length; i++) - id[i] |= 0; - } else if (Array.isArray(id)) { - id = id.slice(); - for (let i = 0; i < id.length; i++) - id[i] |= 0; - } - - if (!Array.isArray(id)) { - return this.reporter.error('objid() should be either array or string, ' + - 'got: ' + JSON.stringify(id)); - } - - if (!relative) { - if (id[1] >= 40) - return this.reporter.error('Second objid identifier OOB'); - id.splice(0, 2, id[0] * 40 + id[1]); - } - - // Count number of octets - let size = 0; - for (let i = 0; i < id.length; i++) { - let ident = id[i]; - for (size++; ident >= 0x80; ident >>= 7) - size++; - } - - const objid = Buffer.alloc(size); - let offset = objid.length - 1; - for (let i = id.length - 1; i >= 0; i--) { - let ident = id[i]; - objid[offset--] = ident & 0x7f; - while ((ident >>= 7) > 0) - objid[offset--] = 0x80 | (ident & 0x7f); - } - - return this._createEncoderBuffer(objid); -}; - -function two(num) { - if (num < 10) - return '0' + num; - else - return num; -} - -DERNode.prototype._encodeTime = function encodeTime(time, tag) { - let str; - const date = new Date(time); - - if (tag === 'gentime') { - str = [ - two(date.getUTCFullYear()), - two(date.getUTCMonth() + 1), - two(date.getUTCDate()), - two(date.getUTCHours()), - two(date.getUTCMinutes()), - two(date.getUTCSeconds()), - 'Z' - ].join(''); - } else if (tag === 'utctime') { - str = [ - two(date.getUTCFullYear() % 100), - two(date.getUTCMonth() + 1), - two(date.getUTCDate()), - two(date.getUTCHours()), - two(date.getUTCMinutes()), - two(date.getUTCSeconds()), - 'Z' - ].join(''); - } else { - this.reporter.error('Encoding ' + tag + ' time is not supported yet'); - } - - return this._encodeStr(str, 'octstr'); -}; - -DERNode.prototype._encodeNull = function encodeNull() { - return this._createEncoderBuffer(''); -}; - -DERNode.prototype._encodeInt = function encodeInt(num, values) { - if (typeof num === 'string') { - if (!values) - return this.reporter.error('String int or enum given, but no values map'); - if (!values.hasOwnProperty(num)) { - return this.reporter.error('Values map doesn\'t contain: ' + - JSON.stringify(num)); - } - num = values[num]; - } - - // Bignum, assume big endian - if (typeof num !== 'number' && !Buffer.isBuffer(num)) { - const numArray = num.toArray(); - if (!num.sign && numArray[0] & 0x80) { - numArray.unshift(0); - } - num = Buffer.from(numArray); - } - - if (Buffer.isBuffer(num)) { - let size = num.length; - if (num.length === 0) - size++; - - const out = Buffer.alloc(size); - num.copy(out); - if (num.length === 0) - out[0] = 0; - return this._createEncoderBuffer(out); - } - - if (num < 0x80) - return this._createEncoderBuffer(num); - - if (num < 0x100) - return this._createEncoderBuffer([0, num]); - - let size = 1; - for (let i = num; i >= 0x100; i >>= 8) - size++; - - const out = new Array(size); - for (let i = out.length - 1; i >= 0; i--) { - out[i] = num & 0xff; - num >>= 8; - } - if(out[0] & 0x80) { - out.unshift(0); - } - - return this._createEncoderBuffer(Buffer.from(out)); -}; - -DERNode.prototype._encodeBool = function encodeBool(value) { - return this._createEncoderBuffer(value ? 0xff : 0); -}; - -DERNode.prototype._use = function use(entity, obj) { - if (typeof entity === 'function') - entity = entity(obj); - return entity._getEncoder('der').tree; -}; - -DERNode.prototype._skipDefault = function skipDefault(dataBuffer, reporter, parent) { - const state = this._baseState; - let i; - if (state['default'] === null) - return false; - - const data = dataBuffer.join(); - if (state.defaultBuffer === undefined) - state.defaultBuffer = this._encodeValue(state['default'], reporter, parent).join(); - - if (data.length !== state.defaultBuffer.length) - return false; - - for (i=0; i < data.length; i++) - if (data[i] !== state.defaultBuffer[i]) - return false; - - return true; -}; - -// Utility methods - -function encodeTag(tag, primitive, cls, reporter) { - let res; - - if (tag === 'seqof') - tag = 'seq'; - else if (tag === 'setof') - tag = 'set'; - - if (der.tagByName.hasOwnProperty(tag)) - res = der.tagByName[tag]; - else if (typeof tag === 'number' && (tag | 0) === tag) - res = tag; - else - return reporter.error('Unknown tag: ' + tag); - - if (res >= 0x1f) - return reporter.error('Multi-octet tag encoding unsupported'); - - if (!primitive) - res |= 0x20; - - res |= (der.tagClassByName[cls || 'universal'] << 6); - - return res; -} - -},{"../base/node":35,"../constants/der":37,"inherits":243,"safer-buffer":391}],43:[function(require,module,exports){ -'use strict'; - -const encoders = exports; - -encoders.der = require('./der'); -encoders.pem = require('./pem'); - -},{"./der":42,"./pem":44}],44:[function(require,module,exports){ -'use strict'; - -const inherits = require('inherits'); - -const DEREncoder = require('./der'); - -function PEMEncoder(entity) { - DEREncoder.call(this, entity); - this.enc = 'pem'; -} -inherits(PEMEncoder, DEREncoder); -module.exports = PEMEncoder; - -PEMEncoder.prototype.encode = function encode(data, options) { - const buf = DEREncoder.prototype.encode.call(this, data); - - const p = buf.toString('base64'); - const out = [ '-----BEGIN ' + options.label + '-----' ]; - for (let i = 0; i < p.length; i += 64) - out.push(p.slice(i, i + 64)); - out.push('-----END ' + options.label + '-----'); - return out.join('\n'); -}; - -},{"./der":42,"inherits":243}],45:[function(require,module,exports){ -(function (module, exports) { - 'use strict'; - - // Utils - function assert (val, msg) { - if (!val) throw new Error(msg || 'Assertion failed'); - } - - // Could use `inherits` module, but don't want to move from single file - // architecture yet. - function inherits (ctor, superCtor) { - ctor.super_ = superCtor; - var TempCtor = function () {}; - TempCtor.prototype = superCtor.prototype; - ctor.prototype = new TempCtor(); - ctor.prototype.constructor = ctor; - } - - // BN - - function BN (number, base, endian) { - if (BN.isBN(number)) { - return number; - } - - this.negative = 0; - this.words = null; - this.length = 0; - - // Reduction context - this.red = null; - - if (number !== null) { - if (base === 'le' || base === 'be') { - endian = base; - base = 10; - } - - this._init(number || 0, base || 10, endian || 'be'); - } - } - if (typeof module === 'object') { - module.exports = BN; - } else { - exports.BN = BN; - } - - BN.BN = BN; - BN.wordSize = 26; - - var Buffer; - try { - if (typeof window !== 'undefined' && typeof window.Buffer !== 'undefined') { - Buffer = window.Buffer; - } else { - Buffer = require('buffer').Buffer; - } - } catch (e) { - } - - BN.isBN = function isBN (num) { - if (num instanceof BN) { - return true; - } - - return num !== null && typeof num === 'object' && - num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); - }; - - BN.max = function max (left, right) { - if (left.cmp(right) > 0) return left; - return right; - }; - - BN.min = function min (left, right) { - if (left.cmp(right) < 0) return left; - return right; - }; - - BN.prototype._init = function init (number, base, endian) { - if (typeof number === 'number') { - return this._initNumber(number, base, endian); - } - - if (typeof number === 'object') { - return this._initArray(number, base, endian); - } - - if (base === 'hex') { - base = 16; - } - assert(base === (base | 0) && base >= 2 && base <= 36); - - number = number.toString().replace(/\s+/g, ''); - var start = 0; - if (number[0] === '-') { - start++; - this.negative = 1; - } - - if (start < number.length) { - if (base === 16) { - this._parseHex(number, start, endian); - } else { - this._parseBase(number, base, start); - if (endian === 'le') { - this._initArray(this.toArray(), base, endian); - } - } - } - }; - - BN.prototype._initNumber = function _initNumber (number, base, endian) { - if (number < 0) { - this.negative = 1; - number = -number; - } - if (number < 0x4000000) { - this.words = [ number & 0x3ffffff ]; - this.length = 1; - } else if (number < 0x10000000000000) { - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff - ]; - this.length = 2; - } else { - assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff, - 1 - ]; - this.length = 3; - } - - if (endian !== 'le') return; - - // Reverse the bytes - this._initArray(this.toArray(), base, endian); - }; - - BN.prototype._initArray = function _initArray (number, base, endian) { - // Perhaps a Uint8Array - assert(typeof number.length === 'number'); - if (number.length <= 0) { - this.words = [ 0 ]; - this.length = 1; - return this; - } - - this.length = Math.ceil(number.length / 3); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - var j, w; - var off = 0; - if (endian === 'be') { - for (i = number.length - 1, j = 0; i >= 0; i -= 3) { - w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } else if (endian === 'le') { - for (i = 0, j = 0; i < number.length; i += 3) { - w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } - return this.strip(); - }; - - function parseHex4Bits (string, index) { - var c = string.charCodeAt(index); - // 'A' - 'F' - if (c >= 65 && c <= 70) { - return c - 55; - // 'a' - 'f' - } else if (c >= 97 && c <= 102) { - return c - 87; - // '0' - '9' - } else { - return (c - 48) & 0xf; - } - } - - function parseHexByte (string, lowerBound, index) { - var r = parseHex4Bits(string, index); - if (index - 1 >= lowerBound) { - r |= parseHex4Bits(string, index - 1) << 4; - } - return r; - } - - BN.prototype._parseHex = function _parseHex (number, start, endian) { - // Create possibly bigger array to ensure that it fits the number - this.length = Math.ceil((number.length - start) / 6); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - // 24-bits chunks - var off = 0; - var j = 0; - - var w; - if (endian === 'be') { - for (i = number.length - 1; i >= start; i -= 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } else { - var parseLength = number.length - start; - for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } - - this.strip(); - }; - - function parseBase (str, start, end, mul) { - var r = 0; - var len = Math.min(str.length, end); - for (var i = start; i < len; i++) { - var c = str.charCodeAt(i) - 48; - - r *= mul; - - // 'a' - if (c >= 49) { - r += c - 49 + 0xa; - - // 'A' - } else if (c >= 17) { - r += c - 17 + 0xa; - - // '0' - '9' - } else { - r += c; - } - } - return r; - } - - BN.prototype._parseBase = function _parseBase (number, base, start) { - // Initialize as zero - this.words = [ 0 ]; - this.length = 1; - - // Find length of limb in base - for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { - limbLen++; - } - limbLen--; - limbPow = (limbPow / base) | 0; - - var total = number.length - start; - var mod = total % limbLen; - var end = Math.min(total, total - mod) + start; - - var word = 0; - for (var i = start; i < end; i += limbLen) { - word = parseBase(number, i, i + limbLen, base); - - this.imuln(limbPow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - if (mod !== 0) { - var pow = 1; - word = parseBase(number, i, number.length, base); - - for (i = 0; i < mod; i++) { - pow *= base; - } - - this.imuln(pow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - this.strip(); - }; - - BN.prototype.copy = function copy (dest) { - dest.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - dest.words[i] = this.words[i]; - } - dest.length = this.length; - dest.negative = this.negative; - dest.red = this.red; - }; - - BN.prototype.clone = function clone () { - var r = new BN(null); - this.copy(r); - return r; - }; - - BN.prototype._expand = function _expand (size) { - while (this.length < size) { - this.words[this.length++] = 0; - } - return this; - }; - - // Remove leading `0` from `this` - BN.prototype.strip = function strip () { - while (this.length > 1 && this.words[this.length - 1] === 0) { - this.length--; - } - return this._normSign(); - }; - - BN.prototype._normSign = function _normSign () { - // -0 = 0 - if (this.length === 1 && this.words[0] === 0) { - this.negative = 0; - } - return this; - }; - - BN.prototype.inspect = function inspect () { - return (this.red ? ''; - }; - - /* - - var zeros = []; - var groupSizes = []; - var groupBases = []; - - var s = ''; - var i = -1; - while (++i < BN.wordSize) { - zeros[i] = s; - s += '0'; - } - groupSizes[0] = 0; - groupSizes[1] = 0; - groupBases[0] = 0; - groupBases[1] = 0; - var base = 2 - 1; - while (++base < 36 + 1) { - var groupSize = 0; - var groupBase = 1; - while (groupBase < (1 << BN.wordSize) / base) { - groupBase *= base; - groupSize += 1; - } - groupSizes[base] = groupSize; - groupBases[base] = groupBase; - } - - */ - - var zeros = [ - '', - '0', - '00', - '000', - '0000', - '00000', - '000000', - '0000000', - '00000000', - '000000000', - '0000000000', - '00000000000', - '000000000000', - '0000000000000', - '00000000000000', - '000000000000000', - '0000000000000000', - '00000000000000000', - '000000000000000000', - '0000000000000000000', - '00000000000000000000', - '000000000000000000000', - '0000000000000000000000', - '00000000000000000000000', - '000000000000000000000000', - '0000000000000000000000000' - ]; - - var groupSizes = [ - 0, 0, - 25, 16, 12, 11, 10, 9, 8, - 8, 7, 7, 7, 7, 6, 6, - 6, 6, 6, 6, 6, 5, 5, - 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5 - ]; - - var groupBases = [ - 0, 0, - 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, - 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, - 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, - 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, - 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 - ]; - - BN.prototype.toString = function toString (base, padding) { - base = base || 10; - padding = padding | 0 || 1; - - var out; - if (base === 16 || base === 'hex') { - out = ''; - var off = 0; - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = this.words[i]; - var word = (((w << off) | carry) & 0xffffff).toString(16); - carry = (w >>> (24 - off)) & 0xffffff; - if (carry !== 0 || i !== this.length - 1) { - out = zeros[6 - word.length] + word + out; - } else { - out = word + out; - } - off += 2; - if (off >= 26) { - off -= 26; - i--; - } - } - if (carry !== 0) { - out = carry.toString(16) + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - if (base === (base | 0) && base >= 2 && base <= 36) { - // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); - var groupSize = groupSizes[base]; - // var groupBase = Math.pow(base, groupSize); - var groupBase = groupBases[base]; - out = ''; - var c = this.clone(); - c.negative = 0; - while (!c.isZero()) { - var r = c.modn(groupBase).toString(base); - c = c.idivn(groupBase); - - if (!c.isZero()) { - out = zeros[groupSize - r.length] + r + out; - } else { - out = r + out; - } - } - if (this.isZero()) { - out = '0' + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - assert(false, 'Base should be between 2 and 36'); - }; - - BN.prototype.toNumber = function toNumber () { - var ret = this.words[0]; - if (this.length === 2) { - ret += this.words[1] * 0x4000000; - } else if (this.length === 3 && this.words[2] === 0x01) { - // NOTE: at this stage it is known that the top bit is set - ret += 0x10000000000000 + (this.words[1] * 0x4000000); - } else if (this.length > 2) { - assert(false, 'Number can only safely store up to 53 bits'); - } - return (this.negative !== 0) ? -ret : ret; - }; - - BN.prototype.toJSON = function toJSON () { - return this.toString(16); - }; - - BN.prototype.toBuffer = function toBuffer (endian, length) { - assert(typeof Buffer !== 'undefined'); - return this.toArrayLike(Buffer, endian, length); - }; - - BN.prototype.toArray = function toArray (endian, length) { - return this.toArrayLike(Array, endian, length); - }; - - BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { - var byteLength = this.byteLength(); - var reqLength = length || Math.max(1, byteLength); - assert(byteLength <= reqLength, 'byte array longer than desired length'); - assert(reqLength > 0, 'Requested array length <= 0'); - - this.strip(); - var littleEndian = endian === 'le'; - var res = new ArrayType(reqLength); - - var b, i; - var q = this.clone(); - if (!littleEndian) { - // Assume big-endian - for (i = 0; i < reqLength - byteLength; i++) { - res[i] = 0; - } - - for (i = 0; !q.isZero(); i++) { - b = q.andln(0xff); - q.iushrn(8); - - res[reqLength - i - 1] = b; - } - } else { - for (i = 0; !q.isZero(); i++) { - b = q.andln(0xff); - q.iushrn(8); - - res[i] = b; - } - - for (; i < reqLength; i++) { - res[i] = 0; - } - } - - return res; - }; - - if (Math.clz32) { - BN.prototype._countBits = function _countBits (w) { - return 32 - Math.clz32(w); - }; - } else { - BN.prototype._countBits = function _countBits (w) { - var t = w; - var r = 0; - if (t >= 0x1000) { - r += 13; - t >>>= 13; - } - if (t >= 0x40) { - r += 7; - t >>>= 7; - } - if (t >= 0x8) { - r += 4; - t >>>= 4; - } - if (t >= 0x02) { - r += 2; - t >>>= 2; - } - return r + t; - }; - } - - BN.prototype._zeroBits = function _zeroBits (w) { - // Short-cut - if (w === 0) return 26; - - var t = w; - var r = 0; - if ((t & 0x1fff) === 0) { - r += 13; - t >>>= 13; - } - if ((t & 0x7f) === 0) { - r += 7; - t >>>= 7; - } - if ((t & 0xf) === 0) { - r += 4; - t >>>= 4; - } - if ((t & 0x3) === 0) { - r += 2; - t >>>= 2; - } - if ((t & 0x1) === 0) { - r++; - } - return r; - }; - - // Return number of used bits in a BN - BN.prototype.bitLength = function bitLength () { - var w = this.words[this.length - 1]; - var hi = this._countBits(w); - return (this.length - 1) * 26 + hi; - }; - - function toBitArray (num) { - var w = new Array(num.bitLength()); - - for (var bit = 0; bit < w.length; bit++) { - var off = (bit / 26) | 0; - var wbit = bit % 26; - - w[bit] = (num.words[off] & (1 << wbit)) >>> wbit; - } - - return w; - } - - // Number of trailing zero bits - BN.prototype.zeroBits = function zeroBits () { - if (this.isZero()) return 0; - - var r = 0; - for (var i = 0; i < this.length; i++) { - var b = this._zeroBits(this.words[i]); - r += b; - if (b !== 26) break; - } - return r; - }; - - BN.prototype.byteLength = function byteLength () { - return Math.ceil(this.bitLength() / 8); - }; - - BN.prototype.toTwos = function toTwos (width) { - if (this.negative !== 0) { - return this.abs().inotn(width).iaddn(1); - } - return this.clone(); - }; - - BN.prototype.fromTwos = function fromTwos (width) { - if (this.testn(width - 1)) { - return this.notn(width).iaddn(1).ineg(); - } - return this.clone(); - }; - - BN.prototype.isNeg = function isNeg () { - return this.negative !== 0; - }; - - // Return negative clone of `this` - BN.prototype.neg = function neg () { - return this.clone().ineg(); - }; - - BN.prototype.ineg = function ineg () { - if (!this.isZero()) { - this.negative ^= 1; - } - - return this; - }; - - // Or `num` with `this` in-place - BN.prototype.iuor = function iuor (num) { - while (this.length < num.length) { - this.words[this.length++] = 0; - } - - for (var i = 0; i < num.length; i++) { - this.words[i] = this.words[i] | num.words[i]; - } - - return this.strip(); - }; - - BN.prototype.ior = function ior (num) { - assert((this.negative | num.negative) === 0); - return this.iuor(num); - }; - - // Or `num` with `this` - BN.prototype.or = function or (num) { - if (this.length > num.length) return this.clone().ior(num); - return num.clone().ior(this); - }; - - BN.prototype.uor = function uor (num) { - if (this.length > num.length) return this.clone().iuor(num); - return num.clone().iuor(this); - }; - - // And `num` with `this` in-place - BN.prototype.iuand = function iuand (num) { - // b = min-length(num, this) - var b; - if (this.length > num.length) { - b = num; - } else { - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = this.words[i] & num.words[i]; - } - - this.length = b.length; - - return this.strip(); - }; - - BN.prototype.iand = function iand (num) { - assert((this.negative | num.negative) === 0); - return this.iuand(num); - }; - - // And `num` with `this` - BN.prototype.and = function and (num) { - if (this.length > num.length) return this.clone().iand(num); - return num.clone().iand(this); - }; - - BN.prototype.uand = function uand (num) { - if (this.length > num.length) return this.clone().iuand(num); - return num.clone().iuand(this); - }; - - // Xor `num` with `this` in-place - BN.prototype.iuxor = function iuxor (num) { - // a.length > b.length - var a; - var b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = a.words[i] ^ b.words[i]; - } - - if (this !== a) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = a.length; - - return this.strip(); - }; - - BN.prototype.ixor = function ixor (num) { - assert((this.negative | num.negative) === 0); - return this.iuxor(num); - }; - - // Xor `num` with `this` - BN.prototype.xor = function xor (num) { - if (this.length > num.length) return this.clone().ixor(num); - return num.clone().ixor(this); - }; - - BN.prototype.uxor = function uxor (num) { - if (this.length > num.length) return this.clone().iuxor(num); - return num.clone().iuxor(this); - }; - - // Not ``this`` with ``width`` bitwidth - BN.prototype.inotn = function inotn (width) { - assert(typeof width === 'number' && width >= 0); - - var bytesNeeded = Math.ceil(width / 26) | 0; - var bitsLeft = width % 26; - - // Extend the buffer with leading zeroes - this._expand(bytesNeeded); - - if (bitsLeft > 0) { - bytesNeeded--; - } - - // Handle complete words - for (var i = 0; i < bytesNeeded; i++) { - this.words[i] = ~this.words[i] & 0x3ffffff; - } - - // Handle the residue - if (bitsLeft > 0) { - this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); - } - - // And remove leading zeroes - return this.strip(); - }; - - BN.prototype.notn = function notn (width) { - return this.clone().inotn(width); - }; - - // Set `bit` of `this` - BN.prototype.setn = function setn (bit, val) { - assert(typeof bit === 'number' && bit >= 0); - - var off = (bit / 26) | 0; - var wbit = bit % 26; - - this._expand(off + 1); - - if (val) { - this.words[off] = this.words[off] | (1 << wbit); - } else { - this.words[off] = this.words[off] & ~(1 << wbit); - } - - return this.strip(); - }; - - // Add `num` to `this` in-place - BN.prototype.iadd = function iadd (num) { - var r; - - // negative + positive - if (this.negative !== 0 && num.negative === 0) { - this.negative = 0; - r = this.isub(num); - this.negative ^= 1; - return this._normSign(); - - // positive + negative - } else if (this.negative === 0 && num.negative !== 0) { - num.negative = 0; - r = this.isub(num); - num.negative = 1; - return r._normSign(); - } - - // a.length > b.length - var a, b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) + (b.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - - this.length = a.length; - if (carry !== 0) { - this.words[this.length] = carry; - this.length++; - // Copy the rest of the words - } else if (a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - return this; - }; - - // Add `num` to `this` - BN.prototype.add = function add (num) { - var res; - if (num.negative !== 0 && this.negative === 0) { - num.negative = 0; - res = this.sub(num); - num.negative ^= 1; - return res; - } else if (num.negative === 0 && this.negative !== 0) { - this.negative = 0; - res = num.sub(this); - this.negative = 1; - return res; - } - - if (this.length > num.length) return this.clone().iadd(num); - - return num.clone().iadd(this); - }; - - // Subtract `num` from `this` in-place - BN.prototype.isub = function isub (num) { - // this - (-num) = this + num - if (num.negative !== 0) { - num.negative = 0; - var r = this.iadd(num); - num.negative = 1; - return r._normSign(); - - // -this - num = -(this + num) - } else if (this.negative !== 0) { - this.negative = 0; - this.iadd(num); - this.negative = 1; - return this._normSign(); - } - - // At this point both numbers are positive - var cmp = this.cmp(num); - - // Optimization - zeroify - if (cmp === 0) { - this.negative = 0; - this.length = 1; - this.words[0] = 0; - return this; - } - - // a > b - var a, b; - if (cmp > 0) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) - (b.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - - // Copy rest of the words - if (carry === 0 && i < a.length && a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = Math.max(this.length, i); - - if (a !== this) { - this.negative = 1; - } - - return this.strip(); - }; - - // Subtract `num` from `this` - BN.prototype.sub = function sub (num) { - return this.clone().isub(num); - }; - - function smallMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - var len = (self.length + num.length) | 0; - out.length = len; - len = (len - 1) | 0; - - // Peel one iteration (compiler can't do it, because of code complexity) - var a = self.words[0] | 0; - var b = num.words[0] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - var carry = (r / 0x4000000) | 0; - out.words[0] = lo; - - for (var k = 1; k < len; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = carry >>> 26; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = (k - j) | 0; - a = self.words[i] | 0; - b = num.words[j] | 0; - r = a * b + rword; - ncarry += (r / 0x4000000) | 0; - rword = r & 0x3ffffff; - } - out.words[k] = rword | 0; - carry = ncarry | 0; - } - if (carry !== 0) { - out.words[k] = carry | 0; - } else { - out.length--; - } - - return out.strip(); - } - - // TODO(indutny): it may be reasonable to omit it for users who don't need - // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit - // multiplication (like elliptic secp256k1). - var comb10MulTo = function comb10MulTo (self, num, out) { - var a = self.words; - var b = num.words; - var o = out.words; - var c = 0; - var lo; - var mid; - var hi; - var a0 = a[0] | 0; - var al0 = a0 & 0x1fff; - var ah0 = a0 >>> 13; - var a1 = a[1] | 0; - var al1 = a1 & 0x1fff; - var ah1 = a1 >>> 13; - var a2 = a[2] | 0; - var al2 = a2 & 0x1fff; - var ah2 = a2 >>> 13; - var a3 = a[3] | 0; - var al3 = a3 & 0x1fff; - var ah3 = a3 >>> 13; - var a4 = a[4] | 0; - var al4 = a4 & 0x1fff; - var ah4 = a4 >>> 13; - var a5 = a[5] | 0; - var al5 = a5 & 0x1fff; - var ah5 = a5 >>> 13; - var a6 = a[6] | 0; - var al6 = a6 & 0x1fff; - var ah6 = a6 >>> 13; - var a7 = a[7] | 0; - var al7 = a7 & 0x1fff; - var ah7 = a7 >>> 13; - var a8 = a[8] | 0; - var al8 = a8 & 0x1fff; - var ah8 = a8 >>> 13; - var a9 = a[9] | 0; - var al9 = a9 & 0x1fff; - var ah9 = a9 >>> 13; - var b0 = b[0] | 0; - var bl0 = b0 & 0x1fff; - var bh0 = b0 >>> 13; - var b1 = b[1] | 0; - var bl1 = b1 & 0x1fff; - var bh1 = b1 >>> 13; - var b2 = b[2] | 0; - var bl2 = b2 & 0x1fff; - var bh2 = b2 >>> 13; - var b3 = b[3] | 0; - var bl3 = b3 & 0x1fff; - var bh3 = b3 >>> 13; - var b4 = b[4] | 0; - var bl4 = b4 & 0x1fff; - var bh4 = b4 >>> 13; - var b5 = b[5] | 0; - var bl5 = b5 & 0x1fff; - var bh5 = b5 >>> 13; - var b6 = b[6] | 0; - var bl6 = b6 & 0x1fff; - var bh6 = b6 >>> 13; - var b7 = b[7] | 0; - var bl7 = b7 & 0x1fff; - var bh7 = b7 >>> 13; - var b8 = b[8] | 0; - var bl8 = b8 & 0x1fff; - var bh8 = b8 >>> 13; - var b9 = b[9] | 0; - var bl9 = b9 & 0x1fff; - var bh9 = b9 >>> 13; - - out.negative = self.negative ^ num.negative; - out.length = 19; - /* k = 0 */ - lo = Math.imul(al0, bl0); - mid = Math.imul(al0, bh0); - mid = (mid + Math.imul(ah0, bl0)) | 0; - hi = Math.imul(ah0, bh0); - var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; - w0 &= 0x3ffffff; - /* k = 1 */ - lo = Math.imul(al1, bl0); - mid = Math.imul(al1, bh0); - mid = (mid + Math.imul(ah1, bl0)) | 0; - hi = Math.imul(ah1, bh0); - lo = (lo + Math.imul(al0, bl1)) | 0; - mid = (mid + Math.imul(al0, bh1)) | 0; - mid = (mid + Math.imul(ah0, bl1)) | 0; - hi = (hi + Math.imul(ah0, bh1)) | 0; - var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; - w1 &= 0x3ffffff; - /* k = 2 */ - lo = Math.imul(al2, bl0); - mid = Math.imul(al2, bh0); - mid = (mid + Math.imul(ah2, bl0)) | 0; - hi = Math.imul(ah2, bh0); - lo = (lo + Math.imul(al1, bl1)) | 0; - mid = (mid + Math.imul(al1, bh1)) | 0; - mid = (mid + Math.imul(ah1, bl1)) | 0; - hi = (hi + Math.imul(ah1, bh1)) | 0; - lo = (lo + Math.imul(al0, bl2)) | 0; - mid = (mid + Math.imul(al0, bh2)) | 0; - mid = (mid + Math.imul(ah0, bl2)) | 0; - hi = (hi + Math.imul(ah0, bh2)) | 0; - var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; - w2 &= 0x3ffffff; - /* k = 3 */ - lo = Math.imul(al3, bl0); - mid = Math.imul(al3, bh0); - mid = (mid + Math.imul(ah3, bl0)) | 0; - hi = Math.imul(ah3, bh0); - lo = (lo + Math.imul(al2, bl1)) | 0; - mid = (mid + Math.imul(al2, bh1)) | 0; - mid = (mid + Math.imul(ah2, bl1)) | 0; - hi = (hi + Math.imul(ah2, bh1)) | 0; - lo = (lo + Math.imul(al1, bl2)) | 0; - mid = (mid + Math.imul(al1, bh2)) | 0; - mid = (mid + Math.imul(ah1, bl2)) | 0; - hi = (hi + Math.imul(ah1, bh2)) | 0; - lo = (lo + Math.imul(al0, bl3)) | 0; - mid = (mid + Math.imul(al0, bh3)) | 0; - mid = (mid + Math.imul(ah0, bl3)) | 0; - hi = (hi + Math.imul(ah0, bh3)) | 0; - var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; - w3 &= 0x3ffffff; - /* k = 4 */ - lo = Math.imul(al4, bl0); - mid = Math.imul(al4, bh0); - mid = (mid + Math.imul(ah4, bl0)) | 0; - hi = Math.imul(ah4, bh0); - lo = (lo + Math.imul(al3, bl1)) | 0; - mid = (mid + Math.imul(al3, bh1)) | 0; - mid = (mid + Math.imul(ah3, bl1)) | 0; - hi = (hi + Math.imul(ah3, bh1)) | 0; - lo = (lo + Math.imul(al2, bl2)) | 0; - mid = (mid + Math.imul(al2, bh2)) | 0; - mid = (mid + Math.imul(ah2, bl2)) | 0; - hi = (hi + Math.imul(ah2, bh2)) | 0; - lo = (lo + Math.imul(al1, bl3)) | 0; - mid = (mid + Math.imul(al1, bh3)) | 0; - mid = (mid + Math.imul(ah1, bl3)) | 0; - hi = (hi + Math.imul(ah1, bh3)) | 0; - lo = (lo + Math.imul(al0, bl4)) | 0; - mid = (mid + Math.imul(al0, bh4)) | 0; - mid = (mid + Math.imul(ah0, bl4)) | 0; - hi = (hi + Math.imul(ah0, bh4)) | 0; - var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; - w4 &= 0x3ffffff; - /* k = 5 */ - lo = Math.imul(al5, bl0); - mid = Math.imul(al5, bh0); - mid = (mid + Math.imul(ah5, bl0)) | 0; - hi = Math.imul(ah5, bh0); - lo = (lo + Math.imul(al4, bl1)) | 0; - mid = (mid + Math.imul(al4, bh1)) | 0; - mid = (mid + Math.imul(ah4, bl1)) | 0; - hi = (hi + Math.imul(ah4, bh1)) | 0; - lo = (lo + Math.imul(al3, bl2)) | 0; - mid = (mid + Math.imul(al3, bh2)) | 0; - mid = (mid + Math.imul(ah3, bl2)) | 0; - hi = (hi + Math.imul(ah3, bh2)) | 0; - lo = (lo + Math.imul(al2, bl3)) | 0; - mid = (mid + Math.imul(al2, bh3)) | 0; - mid = (mid + Math.imul(ah2, bl3)) | 0; - hi = (hi + Math.imul(ah2, bh3)) | 0; - lo = (lo + Math.imul(al1, bl4)) | 0; - mid = (mid + Math.imul(al1, bh4)) | 0; - mid = (mid + Math.imul(ah1, bl4)) | 0; - hi = (hi + Math.imul(ah1, bh4)) | 0; - lo = (lo + Math.imul(al0, bl5)) | 0; - mid = (mid + Math.imul(al0, bh5)) | 0; - mid = (mid + Math.imul(ah0, bl5)) | 0; - hi = (hi + Math.imul(ah0, bh5)) | 0; - var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; - w5 &= 0x3ffffff; - /* k = 6 */ - lo = Math.imul(al6, bl0); - mid = Math.imul(al6, bh0); - mid = (mid + Math.imul(ah6, bl0)) | 0; - hi = Math.imul(ah6, bh0); - lo = (lo + Math.imul(al5, bl1)) | 0; - mid = (mid + Math.imul(al5, bh1)) | 0; - mid = (mid + Math.imul(ah5, bl1)) | 0; - hi = (hi + Math.imul(ah5, bh1)) | 0; - lo = (lo + Math.imul(al4, bl2)) | 0; - mid = (mid + Math.imul(al4, bh2)) | 0; - mid = (mid + Math.imul(ah4, bl2)) | 0; - hi = (hi + Math.imul(ah4, bh2)) | 0; - lo = (lo + Math.imul(al3, bl3)) | 0; - mid = (mid + Math.imul(al3, bh3)) | 0; - mid = (mid + Math.imul(ah3, bl3)) | 0; - hi = (hi + Math.imul(ah3, bh3)) | 0; - lo = (lo + Math.imul(al2, bl4)) | 0; - mid = (mid + Math.imul(al2, bh4)) | 0; - mid = (mid + Math.imul(ah2, bl4)) | 0; - hi = (hi + Math.imul(ah2, bh4)) | 0; - lo = (lo + Math.imul(al1, bl5)) | 0; - mid = (mid + Math.imul(al1, bh5)) | 0; - mid = (mid + Math.imul(ah1, bl5)) | 0; - hi = (hi + Math.imul(ah1, bh5)) | 0; - lo = (lo + Math.imul(al0, bl6)) | 0; - mid = (mid + Math.imul(al0, bh6)) | 0; - mid = (mid + Math.imul(ah0, bl6)) | 0; - hi = (hi + Math.imul(ah0, bh6)) | 0; - var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; - w6 &= 0x3ffffff; - /* k = 7 */ - lo = Math.imul(al7, bl0); - mid = Math.imul(al7, bh0); - mid = (mid + Math.imul(ah7, bl0)) | 0; - hi = Math.imul(ah7, bh0); - lo = (lo + Math.imul(al6, bl1)) | 0; - mid = (mid + Math.imul(al6, bh1)) | 0; - mid = (mid + Math.imul(ah6, bl1)) | 0; - hi = (hi + Math.imul(ah6, bh1)) | 0; - lo = (lo + Math.imul(al5, bl2)) | 0; - mid = (mid + Math.imul(al5, bh2)) | 0; - mid = (mid + Math.imul(ah5, bl2)) | 0; - hi = (hi + Math.imul(ah5, bh2)) | 0; - lo = (lo + Math.imul(al4, bl3)) | 0; - mid = (mid + Math.imul(al4, bh3)) | 0; - mid = (mid + Math.imul(ah4, bl3)) | 0; - hi = (hi + Math.imul(ah4, bh3)) | 0; - lo = (lo + Math.imul(al3, bl4)) | 0; - mid = (mid + Math.imul(al3, bh4)) | 0; - mid = (mid + Math.imul(ah3, bl4)) | 0; - hi = (hi + Math.imul(ah3, bh4)) | 0; - lo = (lo + Math.imul(al2, bl5)) | 0; - mid = (mid + Math.imul(al2, bh5)) | 0; - mid = (mid + Math.imul(ah2, bl5)) | 0; - hi = (hi + Math.imul(ah2, bh5)) | 0; - lo = (lo + Math.imul(al1, bl6)) | 0; - mid = (mid + Math.imul(al1, bh6)) | 0; - mid = (mid + Math.imul(ah1, bl6)) | 0; - hi = (hi + Math.imul(ah1, bh6)) | 0; - lo = (lo + Math.imul(al0, bl7)) | 0; - mid = (mid + Math.imul(al0, bh7)) | 0; - mid = (mid + Math.imul(ah0, bl7)) | 0; - hi = (hi + Math.imul(ah0, bh7)) | 0; - var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; - w7 &= 0x3ffffff; - /* k = 8 */ - lo = Math.imul(al8, bl0); - mid = Math.imul(al8, bh0); - mid = (mid + Math.imul(ah8, bl0)) | 0; - hi = Math.imul(ah8, bh0); - lo = (lo + Math.imul(al7, bl1)) | 0; - mid = (mid + Math.imul(al7, bh1)) | 0; - mid = (mid + Math.imul(ah7, bl1)) | 0; - hi = (hi + Math.imul(ah7, bh1)) | 0; - lo = (lo + Math.imul(al6, bl2)) | 0; - mid = (mid + Math.imul(al6, bh2)) | 0; - mid = (mid + Math.imul(ah6, bl2)) | 0; - hi = (hi + Math.imul(ah6, bh2)) | 0; - lo = (lo + Math.imul(al5, bl3)) | 0; - mid = (mid + Math.imul(al5, bh3)) | 0; - mid = (mid + Math.imul(ah5, bl3)) | 0; - hi = (hi + Math.imul(ah5, bh3)) | 0; - lo = (lo + Math.imul(al4, bl4)) | 0; - mid = (mid + Math.imul(al4, bh4)) | 0; - mid = (mid + Math.imul(ah4, bl4)) | 0; - hi = (hi + Math.imul(ah4, bh4)) | 0; - lo = (lo + Math.imul(al3, bl5)) | 0; - mid = (mid + Math.imul(al3, bh5)) | 0; - mid = (mid + Math.imul(ah3, bl5)) | 0; - hi = (hi + Math.imul(ah3, bh5)) | 0; - lo = (lo + Math.imul(al2, bl6)) | 0; - mid = (mid + Math.imul(al2, bh6)) | 0; - mid = (mid + Math.imul(ah2, bl6)) | 0; - hi = (hi + Math.imul(ah2, bh6)) | 0; - lo = (lo + Math.imul(al1, bl7)) | 0; - mid = (mid + Math.imul(al1, bh7)) | 0; - mid = (mid + Math.imul(ah1, bl7)) | 0; - hi = (hi + Math.imul(ah1, bh7)) | 0; - lo = (lo + Math.imul(al0, bl8)) | 0; - mid = (mid + Math.imul(al0, bh8)) | 0; - mid = (mid + Math.imul(ah0, bl8)) | 0; - hi = (hi + Math.imul(ah0, bh8)) | 0; - var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; - w8 &= 0x3ffffff; - /* k = 9 */ - lo = Math.imul(al9, bl0); - mid = Math.imul(al9, bh0); - mid = (mid + Math.imul(ah9, bl0)) | 0; - hi = Math.imul(ah9, bh0); - lo = (lo + Math.imul(al8, bl1)) | 0; - mid = (mid + Math.imul(al8, bh1)) | 0; - mid = (mid + Math.imul(ah8, bl1)) | 0; - hi = (hi + Math.imul(ah8, bh1)) | 0; - lo = (lo + Math.imul(al7, bl2)) | 0; - mid = (mid + Math.imul(al7, bh2)) | 0; - mid = (mid + Math.imul(ah7, bl2)) | 0; - hi = (hi + Math.imul(ah7, bh2)) | 0; - lo = (lo + Math.imul(al6, bl3)) | 0; - mid = (mid + Math.imul(al6, bh3)) | 0; - mid = (mid + Math.imul(ah6, bl3)) | 0; - hi = (hi + Math.imul(ah6, bh3)) | 0; - lo = (lo + Math.imul(al5, bl4)) | 0; - mid = (mid + Math.imul(al5, bh4)) | 0; - mid = (mid + Math.imul(ah5, bl4)) | 0; - hi = (hi + Math.imul(ah5, bh4)) | 0; - lo = (lo + Math.imul(al4, bl5)) | 0; - mid = (mid + Math.imul(al4, bh5)) | 0; - mid = (mid + Math.imul(ah4, bl5)) | 0; - hi = (hi + Math.imul(ah4, bh5)) | 0; - lo = (lo + Math.imul(al3, bl6)) | 0; - mid = (mid + Math.imul(al3, bh6)) | 0; - mid = (mid + Math.imul(ah3, bl6)) | 0; - hi = (hi + Math.imul(ah3, bh6)) | 0; - lo = (lo + Math.imul(al2, bl7)) | 0; - mid = (mid + Math.imul(al2, bh7)) | 0; - mid = (mid + Math.imul(ah2, bl7)) | 0; - hi = (hi + Math.imul(ah2, bh7)) | 0; - lo = (lo + Math.imul(al1, bl8)) | 0; - mid = (mid + Math.imul(al1, bh8)) | 0; - mid = (mid + Math.imul(ah1, bl8)) | 0; - hi = (hi + Math.imul(ah1, bh8)) | 0; - lo = (lo + Math.imul(al0, bl9)) | 0; - mid = (mid + Math.imul(al0, bh9)) | 0; - mid = (mid + Math.imul(ah0, bl9)) | 0; - hi = (hi + Math.imul(ah0, bh9)) | 0; - var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; - w9 &= 0x3ffffff; - /* k = 10 */ - lo = Math.imul(al9, bl1); - mid = Math.imul(al9, bh1); - mid = (mid + Math.imul(ah9, bl1)) | 0; - hi = Math.imul(ah9, bh1); - lo = (lo + Math.imul(al8, bl2)) | 0; - mid = (mid + Math.imul(al8, bh2)) | 0; - mid = (mid + Math.imul(ah8, bl2)) | 0; - hi = (hi + Math.imul(ah8, bh2)) | 0; - lo = (lo + Math.imul(al7, bl3)) | 0; - mid = (mid + Math.imul(al7, bh3)) | 0; - mid = (mid + Math.imul(ah7, bl3)) | 0; - hi = (hi + Math.imul(ah7, bh3)) | 0; - lo = (lo + Math.imul(al6, bl4)) | 0; - mid = (mid + Math.imul(al6, bh4)) | 0; - mid = (mid + Math.imul(ah6, bl4)) | 0; - hi = (hi + Math.imul(ah6, bh4)) | 0; - lo = (lo + Math.imul(al5, bl5)) | 0; - mid = (mid + Math.imul(al5, bh5)) | 0; - mid = (mid + Math.imul(ah5, bl5)) | 0; - hi = (hi + Math.imul(ah5, bh5)) | 0; - lo = (lo + Math.imul(al4, bl6)) | 0; - mid = (mid + Math.imul(al4, bh6)) | 0; - mid = (mid + Math.imul(ah4, bl6)) | 0; - hi = (hi + Math.imul(ah4, bh6)) | 0; - lo = (lo + Math.imul(al3, bl7)) | 0; - mid = (mid + Math.imul(al3, bh7)) | 0; - mid = (mid + Math.imul(ah3, bl7)) | 0; - hi = (hi + Math.imul(ah3, bh7)) | 0; - lo = (lo + Math.imul(al2, bl8)) | 0; - mid = (mid + Math.imul(al2, bh8)) | 0; - mid = (mid + Math.imul(ah2, bl8)) | 0; - hi = (hi + Math.imul(ah2, bh8)) | 0; - lo = (lo + Math.imul(al1, bl9)) | 0; - mid = (mid + Math.imul(al1, bh9)) | 0; - mid = (mid + Math.imul(ah1, bl9)) | 0; - hi = (hi + Math.imul(ah1, bh9)) | 0; - var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; - w10 &= 0x3ffffff; - /* k = 11 */ - lo = Math.imul(al9, bl2); - mid = Math.imul(al9, bh2); - mid = (mid + Math.imul(ah9, bl2)) | 0; - hi = Math.imul(ah9, bh2); - lo = (lo + Math.imul(al8, bl3)) | 0; - mid = (mid + Math.imul(al8, bh3)) | 0; - mid = (mid + Math.imul(ah8, bl3)) | 0; - hi = (hi + Math.imul(ah8, bh3)) | 0; - lo = (lo + Math.imul(al7, bl4)) | 0; - mid = (mid + Math.imul(al7, bh4)) | 0; - mid = (mid + Math.imul(ah7, bl4)) | 0; - hi = (hi + Math.imul(ah7, bh4)) | 0; - lo = (lo + Math.imul(al6, bl5)) | 0; - mid = (mid + Math.imul(al6, bh5)) | 0; - mid = (mid + Math.imul(ah6, bl5)) | 0; - hi = (hi + Math.imul(ah6, bh5)) | 0; - lo = (lo + Math.imul(al5, bl6)) | 0; - mid = (mid + Math.imul(al5, bh6)) | 0; - mid = (mid + Math.imul(ah5, bl6)) | 0; - hi = (hi + Math.imul(ah5, bh6)) | 0; - lo = (lo + Math.imul(al4, bl7)) | 0; - mid = (mid + Math.imul(al4, bh7)) | 0; - mid = (mid + Math.imul(ah4, bl7)) | 0; - hi = (hi + Math.imul(ah4, bh7)) | 0; - lo = (lo + Math.imul(al3, bl8)) | 0; - mid = (mid + Math.imul(al3, bh8)) | 0; - mid = (mid + Math.imul(ah3, bl8)) | 0; - hi = (hi + Math.imul(ah3, bh8)) | 0; - lo = (lo + Math.imul(al2, bl9)) | 0; - mid = (mid + Math.imul(al2, bh9)) | 0; - mid = (mid + Math.imul(ah2, bl9)) | 0; - hi = (hi + Math.imul(ah2, bh9)) | 0; - var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; - w11 &= 0x3ffffff; - /* k = 12 */ - lo = Math.imul(al9, bl3); - mid = Math.imul(al9, bh3); - mid = (mid + Math.imul(ah9, bl3)) | 0; - hi = Math.imul(ah9, bh3); - lo = (lo + Math.imul(al8, bl4)) | 0; - mid = (mid + Math.imul(al8, bh4)) | 0; - mid = (mid + Math.imul(ah8, bl4)) | 0; - hi = (hi + Math.imul(ah8, bh4)) | 0; - lo = (lo + Math.imul(al7, bl5)) | 0; - mid = (mid + Math.imul(al7, bh5)) | 0; - mid = (mid + Math.imul(ah7, bl5)) | 0; - hi = (hi + Math.imul(ah7, bh5)) | 0; - lo = (lo + Math.imul(al6, bl6)) | 0; - mid = (mid + Math.imul(al6, bh6)) | 0; - mid = (mid + Math.imul(ah6, bl6)) | 0; - hi = (hi + Math.imul(ah6, bh6)) | 0; - lo = (lo + Math.imul(al5, bl7)) | 0; - mid = (mid + Math.imul(al5, bh7)) | 0; - mid = (mid + Math.imul(ah5, bl7)) | 0; - hi = (hi + Math.imul(ah5, bh7)) | 0; - lo = (lo + Math.imul(al4, bl8)) | 0; - mid = (mid + Math.imul(al4, bh8)) | 0; - mid = (mid + Math.imul(ah4, bl8)) | 0; - hi = (hi + Math.imul(ah4, bh8)) | 0; - lo = (lo + Math.imul(al3, bl9)) | 0; - mid = (mid + Math.imul(al3, bh9)) | 0; - mid = (mid + Math.imul(ah3, bl9)) | 0; - hi = (hi + Math.imul(ah3, bh9)) | 0; - var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; - w12 &= 0x3ffffff; - /* k = 13 */ - lo = Math.imul(al9, bl4); - mid = Math.imul(al9, bh4); - mid = (mid + Math.imul(ah9, bl4)) | 0; - hi = Math.imul(ah9, bh4); - lo = (lo + Math.imul(al8, bl5)) | 0; - mid = (mid + Math.imul(al8, bh5)) | 0; - mid = (mid + Math.imul(ah8, bl5)) | 0; - hi = (hi + Math.imul(ah8, bh5)) | 0; - lo = (lo + Math.imul(al7, bl6)) | 0; - mid = (mid + Math.imul(al7, bh6)) | 0; - mid = (mid + Math.imul(ah7, bl6)) | 0; - hi = (hi + Math.imul(ah7, bh6)) | 0; - lo = (lo + Math.imul(al6, bl7)) | 0; - mid = (mid + Math.imul(al6, bh7)) | 0; - mid = (mid + Math.imul(ah6, bl7)) | 0; - hi = (hi + Math.imul(ah6, bh7)) | 0; - lo = (lo + Math.imul(al5, bl8)) | 0; - mid = (mid + Math.imul(al5, bh8)) | 0; - mid = (mid + Math.imul(ah5, bl8)) | 0; - hi = (hi + Math.imul(ah5, bh8)) | 0; - lo = (lo + Math.imul(al4, bl9)) | 0; - mid = (mid + Math.imul(al4, bh9)) | 0; - mid = (mid + Math.imul(ah4, bl9)) | 0; - hi = (hi + Math.imul(ah4, bh9)) | 0; - var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; - w13 &= 0x3ffffff; - /* k = 14 */ - lo = Math.imul(al9, bl5); - mid = Math.imul(al9, bh5); - mid = (mid + Math.imul(ah9, bl5)) | 0; - hi = Math.imul(ah9, bh5); - lo = (lo + Math.imul(al8, bl6)) | 0; - mid = (mid + Math.imul(al8, bh6)) | 0; - mid = (mid + Math.imul(ah8, bl6)) | 0; - hi = (hi + Math.imul(ah8, bh6)) | 0; - lo = (lo + Math.imul(al7, bl7)) | 0; - mid = (mid + Math.imul(al7, bh7)) | 0; - mid = (mid + Math.imul(ah7, bl7)) | 0; - hi = (hi + Math.imul(ah7, bh7)) | 0; - lo = (lo + Math.imul(al6, bl8)) | 0; - mid = (mid + Math.imul(al6, bh8)) | 0; - mid = (mid + Math.imul(ah6, bl8)) | 0; - hi = (hi + Math.imul(ah6, bh8)) | 0; - lo = (lo + Math.imul(al5, bl9)) | 0; - mid = (mid + Math.imul(al5, bh9)) | 0; - mid = (mid + Math.imul(ah5, bl9)) | 0; - hi = (hi + Math.imul(ah5, bh9)) | 0; - var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; - w14 &= 0x3ffffff; - /* k = 15 */ - lo = Math.imul(al9, bl6); - mid = Math.imul(al9, bh6); - mid = (mid + Math.imul(ah9, bl6)) | 0; - hi = Math.imul(ah9, bh6); - lo = (lo + Math.imul(al8, bl7)) | 0; - mid = (mid + Math.imul(al8, bh7)) | 0; - mid = (mid + Math.imul(ah8, bl7)) | 0; - hi = (hi + Math.imul(ah8, bh7)) | 0; - lo = (lo + Math.imul(al7, bl8)) | 0; - mid = (mid + Math.imul(al7, bh8)) | 0; - mid = (mid + Math.imul(ah7, bl8)) | 0; - hi = (hi + Math.imul(ah7, bh8)) | 0; - lo = (lo + Math.imul(al6, bl9)) | 0; - mid = (mid + Math.imul(al6, bh9)) | 0; - mid = (mid + Math.imul(ah6, bl9)) | 0; - hi = (hi + Math.imul(ah6, bh9)) | 0; - var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; - w15 &= 0x3ffffff; - /* k = 16 */ - lo = Math.imul(al9, bl7); - mid = Math.imul(al9, bh7); - mid = (mid + Math.imul(ah9, bl7)) | 0; - hi = Math.imul(ah9, bh7); - lo = (lo + Math.imul(al8, bl8)) | 0; - mid = (mid + Math.imul(al8, bh8)) | 0; - mid = (mid + Math.imul(ah8, bl8)) | 0; - hi = (hi + Math.imul(ah8, bh8)) | 0; - lo = (lo + Math.imul(al7, bl9)) | 0; - mid = (mid + Math.imul(al7, bh9)) | 0; - mid = (mid + Math.imul(ah7, bl9)) | 0; - hi = (hi + Math.imul(ah7, bh9)) | 0; - var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; - w16 &= 0x3ffffff; - /* k = 17 */ - lo = Math.imul(al9, bl8); - mid = Math.imul(al9, bh8); - mid = (mid + Math.imul(ah9, bl8)) | 0; - hi = Math.imul(ah9, bh8); - lo = (lo + Math.imul(al8, bl9)) | 0; - mid = (mid + Math.imul(al8, bh9)) | 0; - mid = (mid + Math.imul(ah8, bl9)) | 0; - hi = (hi + Math.imul(ah8, bh9)) | 0; - var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; - w17 &= 0x3ffffff; - /* k = 18 */ - lo = Math.imul(al9, bl9); - mid = Math.imul(al9, bh9); - mid = (mid + Math.imul(ah9, bl9)) | 0; - hi = Math.imul(ah9, bh9); - var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; - w18 &= 0x3ffffff; - o[0] = w0; - o[1] = w1; - o[2] = w2; - o[3] = w3; - o[4] = w4; - o[5] = w5; - o[6] = w6; - o[7] = w7; - o[8] = w8; - o[9] = w9; - o[10] = w10; - o[11] = w11; - o[12] = w12; - o[13] = w13; - o[14] = w14; - o[15] = w15; - o[16] = w16; - o[17] = w17; - o[18] = w18; - if (c !== 0) { - o[19] = c; - out.length++; - } - return out; - }; - - // Polyfill comb - if (!Math.imul) { - comb10MulTo = smallMulTo; - } - - function bigMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - out.length = self.length + num.length; - - var carry = 0; - var hncarry = 0; - for (var k = 0; k < out.length - 1; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = hncarry; - hncarry = 0; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = k - j; - var a = self.words[i] | 0; - var b = num.words[j] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; - lo = (lo + rword) | 0; - rword = lo & 0x3ffffff; - ncarry = (ncarry + (lo >>> 26)) | 0; - - hncarry += ncarry >>> 26; - ncarry &= 0x3ffffff; - } - out.words[k] = rword; - carry = ncarry; - ncarry = hncarry; - } - if (carry !== 0) { - out.words[k] = carry; - } else { - out.length--; - } - - return out.strip(); - } - - function jumboMulTo (self, num, out) { - var fftm = new FFTM(); - return fftm.mulp(self, num, out); - } - - BN.prototype.mulTo = function mulTo (num, out) { - var res; - var len = this.length + num.length; - if (this.length === 10 && num.length === 10) { - res = comb10MulTo(this, num, out); - } else if (len < 63) { - res = smallMulTo(this, num, out); - } else if (len < 1024) { - res = bigMulTo(this, num, out); - } else { - res = jumboMulTo(this, num, out); - } - - return res; - }; - - // Cooley-Tukey algorithm for FFT - // slightly revisited to rely on looping instead of recursion - - function FFTM (x, y) { - this.x = x; - this.y = y; - } - - FFTM.prototype.makeRBT = function makeRBT (N) { - var t = new Array(N); - var l = BN.prototype._countBits(N) - 1; - for (var i = 0; i < N; i++) { - t[i] = this.revBin(i, l, N); - } - - return t; - }; - - // Returns binary-reversed representation of `x` - FFTM.prototype.revBin = function revBin (x, l, N) { - if (x === 0 || x === N - 1) return x; - - var rb = 0; - for (var i = 0; i < l; i++) { - rb |= (x & 1) << (l - i - 1); - x >>= 1; - } - - return rb; - }; - - // Performs "tweedling" phase, therefore 'emulating' - // behaviour of the recursive algorithm - FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { - for (var i = 0; i < N; i++) { - rtws[i] = rws[rbt[i]]; - itws[i] = iws[rbt[i]]; - } - }; - - FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { - this.permute(rbt, rws, iws, rtws, itws, N); - - for (var s = 1; s < N; s <<= 1) { - var l = s << 1; - - var rtwdf = Math.cos(2 * Math.PI / l); - var itwdf = Math.sin(2 * Math.PI / l); - - for (var p = 0; p < N; p += l) { - var rtwdf_ = rtwdf; - var itwdf_ = itwdf; - - for (var j = 0; j < s; j++) { - var re = rtws[p + j]; - var ie = itws[p + j]; - - var ro = rtws[p + j + s]; - var io = itws[p + j + s]; - - var rx = rtwdf_ * ro - itwdf_ * io; - - io = rtwdf_ * io + itwdf_ * ro; - ro = rx; - - rtws[p + j] = re + ro; - itws[p + j] = ie + io; - - rtws[p + j + s] = re - ro; - itws[p + j + s] = ie - io; - - /* jshint maxdepth : false */ - if (j !== l) { - rx = rtwdf * rtwdf_ - itwdf * itwdf_; - - itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; - rtwdf_ = rx; - } - } - } - } - }; - - FFTM.prototype.guessLen13b = function guessLen13b (n, m) { - var N = Math.max(m, n) | 1; - var odd = N & 1; - var i = 0; - for (N = N / 2 | 0; N; N = N >>> 1) { - i++; - } - - return 1 << i + 1 + odd; - }; - - FFTM.prototype.conjugate = function conjugate (rws, iws, N) { - if (N <= 1) return; - - for (var i = 0; i < N / 2; i++) { - var t = rws[i]; - - rws[i] = rws[N - i - 1]; - rws[N - i - 1] = t; - - t = iws[i]; - - iws[i] = -iws[N - i - 1]; - iws[N - i - 1] = -t; - } - }; - - FFTM.prototype.normalize13b = function normalize13b (ws, N) { - var carry = 0; - for (var i = 0; i < N / 2; i++) { - var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + - Math.round(ws[2 * i] / N) + - carry; - - ws[i] = w & 0x3ffffff; - - if (w < 0x4000000) { - carry = 0; - } else { - carry = w / 0x4000000 | 0; - } - } - - return ws; - }; - - FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { - var carry = 0; - for (var i = 0; i < len; i++) { - carry = carry + (ws[i] | 0); - - rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; - rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; - } - - // Pad with zeroes - for (i = 2 * len; i < N; ++i) { - rws[i] = 0; - } - - assert(carry === 0); - assert((carry & ~0x1fff) === 0); - }; - - FFTM.prototype.stub = function stub (N) { - var ph = new Array(N); - for (var i = 0; i < N; i++) { - ph[i] = 0; - } - - return ph; - }; - - FFTM.prototype.mulp = function mulp (x, y, out) { - var N = 2 * this.guessLen13b(x.length, y.length); - - var rbt = this.makeRBT(N); - - var _ = this.stub(N); - - var rws = new Array(N); - var rwst = new Array(N); - var iwst = new Array(N); - - var nrws = new Array(N); - var nrwst = new Array(N); - var niwst = new Array(N); - - var rmws = out.words; - rmws.length = N; - - this.convert13b(x.words, x.length, rws, N); - this.convert13b(y.words, y.length, nrws, N); - - this.transform(rws, _, rwst, iwst, N, rbt); - this.transform(nrws, _, nrwst, niwst, N, rbt); - - for (var i = 0; i < N; i++) { - var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; - iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; - rwst[i] = rx; - } - - this.conjugate(rwst, iwst, N); - this.transform(rwst, iwst, rmws, _, N, rbt); - this.conjugate(rmws, _, N); - this.normalize13b(rmws, N); - - out.negative = x.negative ^ y.negative; - out.length = x.length + y.length; - return out.strip(); - }; - - // Multiply `this` by `num` - BN.prototype.mul = function mul (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return this.mulTo(num, out); - }; - - // Multiply employing FFT - BN.prototype.mulf = function mulf (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return jumboMulTo(this, num, out); - }; - - // In-place Multiplication - BN.prototype.imul = function imul (num) { - return this.clone().mulTo(num, this); - }; - - BN.prototype.imuln = function imuln (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - - // Carry - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = (this.words[i] | 0) * num; - var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); - carry >>= 26; - carry += (w / 0x4000000) | 0; - // NOTE: lo is 27bit maximum - carry += lo >>> 26; - this.words[i] = lo & 0x3ffffff; - } - - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - - return this; - }; - - BN.prototype.muln = function muln (num) { - return this.clone().imuln(num); - }; - - // `this` * `this` - BN.prototype.sqr = function sqr () { - return this.mul(this); - }; - - // `this` * `this` in-place - BN.prototype.isqr = function isqr () { - return this.imul(this.clone()); - }; - - // Math.pow(`this`, `num`) - BN.prototype.pow = function pow (num) { - var w = toBitArray(num); - if (w.length === 0) return new BN(1); - - // Skip leading zeroes - var res = this; - for (var i = 0; i < w.length; i++, res = res.sqr()) { - if (w[i] !== 0) break; - } - - if (++i < w.length) { - for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { - if (w[i] === 0) continue; - - res = res.mul(q); - } - } - - return res; - }; - - // Shift-left in-place - BN.prototype.iushln = function iushln (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); - var i; - - if (r !== 0) { - var carry = 0; - - for (i = 0; i < this.length; i++) { - var newCarry = this.words[i] & carryMask; - var c = ((this.words[i] | 0) - newCarry) << r; - this.words[i] = c | carry; - carry = newCarry >>> (26 - r); - } - - if (carry) { - this.words[i] = carry; - this.length++; - } - } - - if (s !== 0) { - for (i = this.length - 1; i >= 0; i--) { - this.words[i + s] = this.words[i]; - } - - for (i = 0; i < s; i++) { - this.words[i] = 0; - } - - this.length += s; - } - - return this.strip(); - }; - - BN.prototype.ishln = function ishln (bits) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushln(bits); - }; - - // Shift-right in-place - // NOTE: `hint` is a lowest bit before trailing zeroes - // NOTE: if `extended` is present - it will be filled with destroyed bits - BN.prototype.iushrn = function iushrn (bits, hint, extended) { - assert(typeof bits === 'number' && bits >= 0); - var h; - if (hint) { - h = (hint - (hint % 26)) / 26; - } else { - h = 0; - } - - var r = bits % 26; - var s = Math.min((bits - r) / 26, this.length); - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - var maskedWords = extended; - - h -= s; - h = Math.max(0, h); - - // Extended mode, copy masked part - if (maskedWords) { - for (var i = 0; i < s; i++) { - maskedWords.words[i] = this.words[i]; - } - maskedWords.length = s; - } - - if (s === 0) { - // No-op, we should not move anything at all - } else if (this.length > s) { - this.length -= s; - for (i = 0; i < this.length; i++) { - this.words[i] = this.words[i + s]; - } - } else { - this.words[0] = 0; - this.length = 1; - } - - var carry = 0; - for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { - var word = this.words[i] | 0; - this.words[i] = (carry << (26 - r)) | (word >>> r); - carry = word & mask; - } - - // Push carried bits as a mask - if (maskedWords && carry !== 0) { - maskedWords.words[maskedWords.length++] = carry; - } - - if (this.length === 0) { - this.words[0] = 0; - this.length = 1; - } - - return this.strip(); - }; - - BN.prototype.ishrn = function ishrn (bits, hint, extended) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushrn(bits, hint, extended); - }; - - // Shift-left - BN.prototype.shln = function shln (bits) { - return this.clone().ishln(bits); - }; - - BN.prototype.ushln = function ushln (bits) { - return this.clone().iushln(bits); - }; - - // Shift-right - BN.prototype.shrn = function shrn (bits) { - return this.clone().ishrn(bits); - }; - - BN.prototype.ushrn = function ushrn (bits) { - return this.clone().iushrn(bits); - }; - - // Test if n bit is set - BN.prototype.testn = function testn (bit) { - assert(typeof bit === 'number' && bit >= 0); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) return false; - - // Check bit and return - var w = this.words[s]; - - return !!(w & q); - }; - - // Return only lowers bits of number (in-place) - BN.prototype.imaskn = function imaskn (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - - assert(this.negative === 0, 'imaskn works only with positive numbers'); - - if (this.length <= s) { - return this; - } - - if (r !== 0) { - s++; - } - this.length = Math.min(s, this.length); - - if (r !== 0) { - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - this.words[this.length - 1] &= mask; - } - - return this.strip(); - }; - - // Return only lowers bits of number - BN.prototype.maskn = function maskn (bits) { - return this.clone().imaskn(bits); - }; - - // Add plain number `num` to `this` - BN.prototype.iaddn = function iaddn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.isubn(-num); - - // Possible sign change - if (this.negative !== 0) { - if (this.length === 1 && (this.words[0] | 0) < num) { - this.words[0] = num - (this.words[0] | 0); - this.negative = 0; - return this; - } - - this.negative = 0; - this.isubn(num); - this.negative = 1; - return this; - } - - // Add without checks - return this._iaddn(num); - }; - - BN.prototype._iaddn = function _iaddn (num) { - this.words[0] += num; - - // Carry - for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { - this.words[i] -= 0x4000000; - if (i === this.length - 1) { - this.words[i + 1] = 1; - } else { - this.words[i + 1]++; - } - } - this.length = Math.max(this.length, i + 1); - - return this; - }; - - // Subtract plain number `num` from `this` - BN.prototype.isubn = function isubn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.iaddn(-num); - - if (this.negative !== 0) { - this.negative = 0; - this.iaddn(num); - this.negative = 1; - return this; - } - - this.words[0] -= num; - - if (this.length === 1 && this.words[0] < 0) { - this.words[0] = -this.words[0]; - this.negative = 1; - } else { - // Carry - for (var i = 0; i < this.length && this.words[i] < 0; i++) { - this.words[i] += 0x4000000; - this.words[i + 1] -= 1; - } - } - - return this.strip(); - }; - - BN.prototype.addn = function addn (num) { - return this.clone().iaddn(num); - }; - - BN.prototype.subn = function subn (num) { - return this.clone().isubn(num); - }; - - BN.prototype.iabs = function iabs () { - this.negative = 0; - - return this; - }; - - BN.prototype.abs = function abs () { - return this.clone().iabs(); - }; - - BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { - var len = num.length + shift; - var i; - - this._expand(len); - - var w; - var carry = 0; - for (i = 0; i < num.length; i++) { - w = (this.words[i + shift] | 0) + carry; - var right = (num.words[i] | 0) * mul; - w -= right & 0x3ffffff; - carry = (w >> 26) - ((right / 0x4000000) | 0); - this.words[i + shift] = w & 0x3ffffff; - } - for (; i < this.length - shift; i++) { - w = (this.words[i + shift] | 0) + carry; - carry = w >> 26; - this.words[i + shift] = w & 0x3ffffff; - } - - if (carry === 0) return this.strip(); - - // Subtraction overflow - assert(carry === -1); - carry = 0; - for (i = 0; i < this.length; i++) { - w = -(this.words[i] | 0) + carry; - carry = w >> 26; - this.words[i] = w & 0x3ffffff; - } - this.negative = 1; - - return this.strip(); - }; - - BN.prototype._wordDiv = function _wordDiv (num, mode) { - var shift = this.length - num.length; - - var a = this.clone(); - var b = num; - - // Normalize - var bhi = b.words[b.length - 1] | 0; - var bhiBits = this._countBits(bhi); - shift = 26 - bhiBits; - if (shift !== 0) { - b = b.ushln(shift); - a.iushln(shift); - bhi = b.words[b.length - 1] | 0; - } - - // Initialize quotient - var m = a.length - b.length; - var q; - - if (mode !== 'mod') { - q = new BN(null); - q.length = m + 1; - q.words = new Array(q.length); - for (var i = 0; i < q.length; i++) { - q.words[i] = 0; - } - } - - var diff = a.clone()._ishlnsubmul(b, 1, m); - if (diff.negative === 0) { - a = diff; - if (q) { - q.words[m] = 1; - } - } - - for (var j = m - 1; j >= 0; j--) { - var qj = (a.words[b.length + j] | 0) * 0x4000000 + - (a.words[b.length + j - 1] | 0); - - // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max - // (0x7ffffff) - qj = Math.min((qj / bhi) | 0, 0x3ffffff); - - a._ishlnsubmul(b, qj, j); - while (a.negative !== 0) { - qj--; - a.negative = 0; - a._ishlnsubmul(b, 1, j); - if (!a.isZero()) { - a.negative ^= 1; - } - } - if (q) { - q.words[j] = qj; - } - } - if (q) { - q.strip(); - } - a.strip(); - - // Denormalize - if (mode !== 'div' && shift !== 0) { - a.iushrn(shift); - } - - return { - div: q || null, - mod: a - }; - }; - - // NOTE: 1) `mode` can be set to `mod` to request mod only, - // to `div` to request div only, or be absent to - // request both div & mod - // 2) `positive` is true if unsigned mod is requested - BN.prototype.divmod = function divmod (num, mode, positive) { - assert(!num.isZero()); - - if (this.isZero()) { - return { - div: new BN(0), - mod: new BN(0) - }; - } - - var div, mod, res; - if (this.negative !== 0 && num.negative === 0) { - res = this.neg().divmod(num, mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.iadd(num); - } - } - - return { - div: div, - mod: mod - }; - } - - if (this.negative === 0 && num.negative !== 0) { - res = this.divmod(num.neg(), mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - return { - div: div, - mod: res.mod - }; - } - - if ((this.negative & num.negative) !== 0) { - res = this.neg().divmod(num.neg(), mode); - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.isub(num); - } - } - - return { - div: res.div, - mod: mod - }; - } - - // Both numbers are positive at this point - - // Strip both numbers to approximate shift value - if (num.length > this.length || this.cmp(num) < 0) { - return { - div: new BN(0), - mod: this - }; - } - - // Very short reduction - if (num.length === 1) { - if (mode === 'div') { - return { - div: this.divn(num.words[0]), - mod: null - }; - } - - if (mode === 'mod') { - return { - div: null, - mod: new BN(this.modn(num.words[0])) - }; - } - - return { - div: this.divn(num.words[0]), - mod: new BN(this.modn(num.words[0])) - }; - } - - return this._wordDiv(num, mode); - }; - - // Find `this` / `num` - BN.prototype.div = function div (num) { - return this.divmod(num, 'div', false).div; - }; - - // Find `this` % `num` - BN.prototype.mod = function mod (num) { - return this.divmod(num, 'mod', false).mod; - }; - - BN.prototype.umod = function umod (num) { - return this.divmod(num, 'mod', true).mod; - }; - - // Find Round(`this` / `num`) - BN.prototype.divRound = function divRound (num) { - var dm = this.divmod(num); - - // Fast case - exact division - if (dm.mod.isZero()) return dm.div; - - var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; - - var half = num.ushrn(1); - var r2 = num.andln(1); - var cmp = mod.cmp(half); - - // Round down - if (cmp < 0 || r2 === 1 && cmp === 0) return dm.div; - - // Round up - return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); - }; - - BN.prototype.modn = function modn (num) { - assert(num <= 0x3ffffff); - var p = (1 << 26) % num; - - var acc = 0; - for (var i = this.length - 1; i >= 0; i--) { - acc = (p * acc + (this.words[i] | 0)) % num; - } - - return acc; - }; - - // In-place division by number - BN.prototype.idivn = function idivn (num) { - assert(num <= 0x3ffffff); - - var carry = 0; - for (var i = this.length - 1; i >= 0; i--) { - var w = (this.words[i] | 0) + carry * 0x4000000; - this.words[i] = (w / num) | 0; - carry = w % num; - } - - return this.strip(); - }; - - BN.prototype.divn = function divn (num) { - return this.clone().idivn(num); - }; - - BN.prototype.egcd = function egcd (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var x = this; - var y = p.clone(); - - if (x.negative !== 0) { - x = x.umod(p); - } else { - x = x.clone(); - } - - // A * x + B * y = x - var A = new BN(1); - var B = new BN(0); - - // C * x + D * y = y - var C = new BN(0); - var D = new BN(1); - - var g = 0; - - while (x.isEven() && y.isEven()) { - x.iushrn(1); - y.iushrn(1); - ++g; - } - - var yp = y.clone(); - var xp = x.clone(); - - while (!x.isZero()) { - for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - x.iushrn(i); - while (i-- > 0) { - if (A.isOdd() || B.isOdd()) { - A.iadd(yp); - B.isub(xp); - } - - A.iushrn(1); - B.iushrn(1); - } - } - - for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - y.iushrn(j); - while (j-- > 0) { - if (C.isOdd() || D.isOdd()) { - C.iadd(yp); - D.isub(xp); - } - - C.iushrn(1); - D.iushrn(1); - } - } - - if (x.cmp(y) >= 0) { - x.isub(y); - A.isub(C); - B.isub(D); - } else { - y.isub(x); - C.isub(A); - D.isub(B); - } - } - - return { - a: C, - b: D, - gcd: y.iushln(g) - }; - }; - - // This is reduced incarnation of the binary EEA - // above, designated to invert members of the - // _prime_ fields F(p) at a maximal speed - BN.prototype._invmp = function _invmp (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var a = this; - var b = p.clone(); - - if (a.negative !== 0) { - a = a.umod(p); - } else { - a = a.clone(); - } - - var x1 = new BN(1); - var x2 = new BN(0); - - var delta = b.clone(); - - while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { - for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - a.iushrn(i); - while (i-- > 0) { - if (x1.isOdd()) { - x1.iadd(delta); - } - - x1.iushrn(1); - } - } - - for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - b.iushrn(j); - while (j-- > 0) { - if (x2.isOdd()) { - x2.iadd(delta); - } - - x2.iushrn(1); - } - } - - if (a.cmp(b) >= 0) { - a.isub(b); - x1.isub(x2); - } else { - b.isub(a); - x2.isub(x1); - } - } - - var res; - if (a.cmpn(1) === 0) { - res = x1; - } else { - res = x2; - } - - if (res.cmpn(0) < 0) { - res.iadd(p); - } - - return res; - }; - - BN.prototype.gcd = function gcd (num) { - if (this.isZero()) return num.abs(); - if (num.isZero()) return this.abs(); - - var a = this.clone(); - var b = num.clone(); - a.negative = 0; - b.negative = 0; - - // Remove common factor of two - for (var shift = 0; a.isEven() && b.isEven(); shift++) { - a.iushrn(1); - b.iushrn(1); - } - - do { - while (a.isEven()) { - a.iushrn(1); - } - while (b.isEven()) { - b.iushrn(1); - } - - var r = a.cmp(b); - if (r < 0) { - // Swap `a` and `b` to make `a` always bigger than `b` - var t = a; - a = b; - b = t; - } else if (r === 0 || b.cmpn(1) === 0) { - break; - } - - a.isub(b); - } while (true); - - return b.iushln(shift); - }; - - // Invert number in the field F(num) - BN.prototype.invm = function invm (num) { - return this.egcd(num).a.umod(num); - }; - - BN.prototype.isEven = function isEven () { - return (this.words[0] & 1) === 0; - }; - - BN.prototype.isOdd = function isOdd () { - return (this.words[0] & 1) === 1; - }; - - // And first word and num - BN.prototype.andln = function andln (num) { - return this.words[0] & num; - }; - - // Increment at the bit position in-line - BN.prototype.bincn = function bincn (bit) { - assert(typeof bit === 'number'); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) { - this._expand(s + 1); - this.words[s] |= q; - return this; - } - - // Add bit and propagate, if needed - var carry = q; - for (var i = s; carry !== 0 && i < this.length; i++) { - var w = this.words[i] | 0; - w += carry; - carry = w >>> 26; - w &= 0x3ffffff; - this.words[i] = w; - } - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - return this; - }; - - BN.prototype.isZero = function isZero () { - return this.length === 1 && this.words[0] === 0; - }; - - BN.prototype.cmpn = function cmpn (num) { - var negative = num < 0; - - if (this.negative !== 0 && !negative) return -1; - if (this.negative === 0 && negative) return 1; - - this.strip(); - - var res; - if (this.length > 1) { - res = 1; - } else { - if (negative) { - num = -num; - } - - assert(num <= 0x3ffffff, 'Number is too big'); - - var w = this.words[0] | 0; - res = w === num ? 0 : w < num ? -1 : 1; - } - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Compare two numbers and return: - // 1 - if `this` > `num` - // 0 - if `this` == `num` - // -1 - if `this` < `num` - BN.prototype.cmp = function cmp (num) { - if (this.negative !== 0 && num.negative === 0) return -1; - if (this.negative === 0 && num.negative !== 0) return 1; - - var res = this.ucmp(num); - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Unsigned comparison - BN.prototype.ucmp = function ucmp (num) { - // At this point both numbers have the same sign - if (this.length > num.length) return 1; - if (this.length < num.length) return -1; - - var res = 0; - for (var i = this.length - 1; i >= 0; i--) { - var a = this.words[i] | 0; - var b = num.words[i] | 0; - - if (a === b) continue; - if (a < b) { - res = -1; - } else if (a > b) { - res = 1; - } - break; - } - return res; - }; - - BN.prototype.gtn = function gtn (num) { - return this.cmpn(num) === 1; - }; - - BN.prototype.gt = function gt (num) { - return this.cmp(num) === 1; - }; - - BN.prototype.gten = function gten (num) { - return this.cmpn(num) >= 0; - }; - - BN.prototype.gte = function gte (num) { - return this.cmp(num) >= 0; - }; - - BN.prototype.ltn = function ltn (num) { - return this.cmpn(num) === -1; - }; - - BN.prototype.lt = function lt (num) { - return this.cmp(num) === -1; - }; - - BN.prototype.lten = function lten (num) { - return this.cmpn(num) <= 0; - }; - - BN.prototype.lte = function lte (num) { - return this.cmp(num) <= 0; - }; - - BN.prototype.eqn = function eqn (num) { - return this.cmpn(num) === 0; - }; - - BN.prototype.eq = function eq (num) { - return this.cmp(num) === 0; - }; - - // - // A reduce context, could be using montgomery or something better, depending - // on the `m` itself. - // - BN.red = function red (num) { - return new Red(num); - }; - - BN.prototype.toRed = function toRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - assert(this.negative === 0, 'red works only with positives'); - return ctx.convertTo(this)._forceRed(ctx); - }; - - BN.prototype.fromRed = function fromRed () { - assert(this.red, 'fromRed works only with numbers in reduction context'); - return this.red.convertFrom(this); - }; - - BN.prototype._forceRed = function _forceRed (ctx) { - this.red = ctx; - return this; - }; - - BN.prototype.forceRed = function forceRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - return this._forceRed(ctx); - }; - - BN.prototype.redAdd = function redAdd (num) { - assert(this.red, 'redAdd works only with red numbers'); - return this.red.add(this, num); - }; - - BN.prototype.redIAdd = function redIAdd (num) { - assert(this.red, 'redIAdd works only with red numbers'); - return this.red.iadd(this, num); - }; - - BN.prototype.redSub = function redSub (num) { - assert(this.red, 'redSub works only with red numbers'); - return this.red.sub(this, num); - }; - - BN.prototype.redISub = function redISub (num) { - assert(this.red, 'redISub works only with red numbers'); - return this.red.isub(this, num); - }; - - BN.prototype.redShl = function redShl (num) { - assert(this.red, 'redShl works only with red numbers'); - return this.red.shl(this, num); - }; - - BN.prototype.redMul = function redMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.mul(this, num); - }; - - BN.prototype.redIMul = function redIMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.imul(this, num); - }; - - BN.prototype.redSqr = function redSqr () { - assert(this.red, 'redSqr works only with red numbers'); - this.red._verify1(this); - return this.red.sqr(this); - }; - - BN.prototype.redISqr = function redISqr () { - assert(this.red, 'redISqr works only with red numbers'); - this.red._verify1(this); - return this.red.isqr(this); - }; - - // Square root over p - BN.prototype.redSqrt = function redSqrt () { - assert(this.red, 'redSqrt works only with red numbers'); - this.red._verify1(this); - return this.red.sqrt(this); - }; - - BN.prototype.redInvm = function redInvm () { - assert(this.red, 'redInvm works only with red numbers'); - this.red._verify1(this); - return this.red.invm(this); - }; - - // Return negative clone of `this` % `red modulo` - BN.prototype.redNeg = function redNeg () { - assert(this.red, 'redNeg works only with red numbers'); - this.red._verify1(this); - return this.red.neg(this); - }; - - BN.prototype.redPow = function redPow (num) { - assert(this.red && !num.red, 'redPow(normalNum)'); - this.red._verify1(this); - return this.red.pow(this, num); - }; - - // Prime numbers with efficient reduction - var primes = { - k256: null, - p224: null, - p192: null, - p25519: null - }; - - // Pseudo-Mersenne prime - function MPrime (name, p) { - // P = 2 ^ N - K - this.name = name; - this.p = new BN(p, 16); - this.n = this.p.bitLength(); - this.k = new BN(1).iushln(this.n).isub(this.p); - - this.tmp = this._tmp(); - } - - MPrime.prototype._tmp = function _tmp () { - var tmp = new BN(null); - tmp.words = new Array(Math.ceil(this.n / 13)); - return tmp; - }; - - MPrime.prototype.ireduce = function ireduce (num) { - // Assumes that `num` is less than `P^2` - // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) - var r = num; - var rlen; - - do { - this.split(r, this.tmp); - r = this.imulK(r); - r = r.iadd(this.tmp); - rlen = r.bitLength(); - } while (rlen > this.n); - - var cmp = rlen < this.n ? -1 : r.ucmp(this.p); - if (cmp === 0) { - r.words[0] = 0; - r.length = 1; - } else if (cmp > 0) { - r.isub(this.p); - } else { - if (r.strip !== undefined) { - // r is BN v4 instance - r.strip(); - } else { - // r is BN v5 instance - r._strip(); - } - } - - return r; - }; - - MPrime.prototype.split = function split (input, out) { - input.iushrn(this.n, 0, out); - }; - - MPrime.prototype.imulK = function imulK (num) { - return num.imul(this.k); - }; - - function K256 () { - MPrime.call( - this, - 'k256', - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); - } - inherits(K256, MPrime); - - K256.prototype.split = function split (input, output) { - // 256 = 9 * 26 + 22 - var mask = 0x3fffff; - - var outLen = Math.min(input.length, 9); - for (var i = 0; i < outLen; i++) { - output.words[i] = input.words[i]; - } - output.length = outLen; - - if (input.length <= 9) { - input.words[0] = 0; - input.length = 1; - return; - } - - // Shift by 9 limbs - var prev = input.words[9]; - output.words[output.length++] = prev & mask; - - for (i = 10; i < input.length; i++) { - var next = input.words[i] | 0; - input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); - prev = next; - } - prev >>>= 22; - input.words[i - 10] = prev; - if (prev === 0 && input.length > 10) { - input.length -= 10; - } else { - input.length -= 9; - } - }; - - K256.prototype.imulK = function imulK (num) { - // K = 0x1000003d1 = [ 0x40, 0x3d1 ] - num.words[num.length] = 0; - num.words[num.length + 1] = 0; - num.length += 2; - - // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 - var lo = 0; - for (var i = 0; i < num.length; i++) { - var w = num.words[i] | 0; - lo += w * 0x3d1; - num.words[i] = lo & 0x3ffffff; - lo = w * 0x40 + ((lo / 0x4000000) | 0); - } - - // Fast length reduction - if (num.words[num.length - 1] === 0) { - num.length--; - if (num.words[num.length - 1] === 0) { - num.length--; - } - } - return num; - }; - - function P224 () { - MPrime.call( - this, - 'p224', - 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); - } - inherits(P224, MPrime); - - function P192 () { - MPrime.call( - this, - 'p192', - 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); - } - inherits(P192, MPrime); - - function P25519 () { - // 2 ^ 255 - 19 - MPrime.call( - this, - '25519', - '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); - } - inherits(P25519, MPrime); - - P25519.prototype.imulK = function imulK (num) { - // K = 0x13 - var carry = 0; - for (var i = 0; i < num.length; i++) { - var hi = (num.words[i] | 0) * 0x13 + carry; - var lo = hi & 0x3ffffff; - hi >>>= 26; - - num.words[i] = lo; - carry = hi; - } - if (carry !== 0) { - num.words[num.length++] = carry; - } - return num; - }; - - // Exported mostly for testing purposes, use plain name instead - BN._prime = function prime (name) { - // Cached version of prime - if (primes[name]) return primes[name]; - - var prime; - if (name === 'k256') { - prime = new K256(); - } else if (name === 'p224') { - prime = new P224(); - } else if (name === 'p192') { - prime = new P192(); - } else if (name === 'p25519') { - prime = new P25519(); - } else { - throw new Error('Unknown prime ' + name); - } - primes[name] = prime; - - return prime; - }; - - // - // Base reduction engine - // - function Red (m) { - if (typeof m === 'string') { - var prime = BN._prime(m); - this.m = prime.p; - this.prime = prime; - } else { - assert(m.gtn(1), 'modulus must be greater than 1'); - this.m = m; - this.prime = null; - } - } - - Red.prototype._verify1 = function _verify1 (a) { - assert(a.negative === 0, 'red works only with positives'); - assert(a.red, 'red works only with red numbers'); - }; - - Red.prototype._verify2 = function _verify2 (a, b) { - assert((a.negative | b.negative) === 0, 'red works only with positives'); - assert(a.red && a.red === b.red, - 'red works only with red numbers'); - }; - - Red.prototype.imod = function imod (a) { - if (this.prime) return this.prime.ireduce(a)._forceRed(this); - return a.umod(this.m)._forceRed(this); - }; - - Red.prototype.neg = function neg (a) { - if (a.isZero()) { - return a.clone(); - } - - return this.m.sub(a)._forceRed(this); - }; - - Red.prototype.add = function add (a, b) { - this._verify2(a, b); - - var res = a.add(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.iadd = function iadd (a, b) { - this._verify2(a, b); - - var res = a.iadd(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res; - }; - - Red.prototype.sub = function sub (a, b) { - this._verify2(a, b); - - var res = a.sub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.isub = function isub (a, b) { - this._verify2(a, b); - - var res = a.isub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res; - }; - - Red.prototype.shl = function shl (a, num) { - this._verify1(a); - return this.imod(a.ushln(num)); - }; - - Red.prototype.imul = function imul (a, b) { - this._verify2(a, b); - return this.imod(a.imul(b)); - }; - - Red.prototype.mul = function mul (a, b) { - this._verify2(a, b); - return this.imod(a.mul(b)); - }; - - Red.prototype.isqr = function isqr (a) { - return this.imul(a, a.clone()); - }; - - Red.prototype.sqr = function sqr (a) { - return this.mul(a, a); - }; - - Red.prototype.sqrt = function sqrt (a) { - if (a.isZero()) return a.clone(); - - var mod3 = this.m.andln(3); - assert(mod3 % 2 === 1); - - // Fast case - if (mod3 === 3) { - var pow = this.m.add(new BN(1)).iushrn(2); - return this.pow(a, pow); - } - - // Tonelli-Shanks algorithm (Totally unoptimized and slow) - // - // Find Q and S, that Q * 2 ^ S = (P - 1) - var q = this.m.subn(1); - var s = 0; - while (!q.isZero() && q.andln(1) === 0) { - s++; - q.iushrn(1); - } - assert(!q.isZero()); - - var one = new BN(1).toRed(this); - var nOne = one.redNeg(); - - // Find quadratic non-residue - // NOTE: Max is such because of generalized Riemann hypothesis. - var lpow = this.m.subn(1).iushrn(1); - var z = this.m.bitLength(); - z = new BN(2 * z * z).toRed(this); - - while (this.pow(z, lpow).cmp(nOne) !== 0) { - z.redIAdd(nOne); - } - - var c = this.pow(z, q); - var r = this.pow(a, q.addn(1).iushrn(1)); - var t = this.pow(a, q); - var m = s; - while (t.cmp(one) !== 0) { - var tmp = t; - for (var i = 0; tmp.cmp(one) !== 0; i++) { - tmp = tmp.redSqr(); - } - assert(i < m); - var b = this.pow(c, new BN(1).iushln(m - i - 1)); - - r = r.redMul(b); - c = b.redSqr(); - t = t.redMul(c); - m = i; - } - - return r; - }; - - Red.prototype.invm = function invm (a) { - var inv = a._invmp(this.m); - if (inv.negative !== 0) { - inv.negative = 0; - return this.imod(inv).redNeg(); - } else { - return this.imod(inv); - } - }; - - Red.prototype.pow = function pow (a, num) { - if (num.isZero()) return new BN(1).toRed(this); - if (num.cmpn(1) === 0) return a.clone(); - - var windowSize = 4; - var wnd = new Array(1 << windowSize); - wnd[0] = new BN(1).toRed(this); - wnd[1] = a; - for (var i = 2; i < wnd.length; i++) { - wnd[i] = this.mul(wnd[i - 1], a); - } - - var res = wnd[0]; - var current = 0; - var currentLen = 0; - var start = num.bitLength() % 26; - if (start === 0) { - start = 26; - } - - for (i = num.length - 1; i >= 0; i--) { - var word = num.words[i]; - for (var j = start - 1; j >= 0; j--) { - var bit = (word >> j) & 1; - if (res !== wnd[0]) { - res = this.sqr(res); - } - - if (bit === 0 && current === 0) { - currentLen = 0; - continue; - } - - current <<= 1; - current |= bit; - currentLen++; - if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; - - res = this.mul(res, wnd[current]); - currentLen = 0; - current = 0; - } - start = 26; - } - - return res; - }; - - Red.prototype.convertTo = function convertTo (num) { - var r = num.umod(this.m); - - return r === num ? r.clone() : r; - }; - - Red.prototype.convertFrom = function convertFrom (num) { - var res = num.clone(); - res.red = null; - return res; - }; - - // - // Montgomery method engine - // - - BN.mont = function mont (num) { - return new Mont(num); - }; - - function Mont (m) { - Red.call(this, m); - - this.shift = this.m.bitLength(); - if (this.shift % 26 !== 0) { - this.shift += 26 - (this.shift % 26); - } - - this.r = new BN(1).iushln(this.shift); - this.r2 = this.imod(this.r.sqr()); - this.rinv = this.r._invmp(this.m); - - this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); - this.minv = this.minv.umod(this.r); - this.minv = this.r.sub(this.minv); - } - inherits(Mont, Red); - - Mont.prototype.convertTo = function convertTo (num) { - return this.imod(num.ushln(this.shift)); - }; - - Mont.prototype.convertFrom = function convertFrom (num) { - var r = this.imod(num.mul(this.rinv)); - r.red = null; - return r; - }; - - Mont.prototype.imul = function imul (a, b) { - if (a.isZero() || b.isZero()) { - a.words[0] = 0; - a.length = 1; - return a; - } - - var t = a.imul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.mul = function mul (a, b) { - if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); - - var t = a.mul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.invm = function invm (a) { - // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R - var res = this.imod(a._invmp(this.m).mul(this.r2)); - return res._forceRed(this); - }; -})(typeof module === 'undefined' || module, this); - -},{"buffer":91}],46:[function(require,module,exports){ -const { encodeChange } = require('./columnar') -const { BackendDoc } = require('./new') -const { backendState } = require('./util') - -/** - * Returns an empty node state. - */ -function init() { - return {state: new BackendDoc(), heads: []} -} - -function clone(backend) { - return {state: backendState(backend).clone(), heads: backend.heads} -} - -function free(backend) { - backend.state = null - backend.frozen = true -} - -/** - * Applies a list of `changes` from remote nodes to the node state `backend`. - * Returns a two-element array `[state, patch]` where `state` is the updated - * node state, and `patch` describes the modifications that need to be made - * to the document objects to reflect these changes. - */ -function applyChanges(backend, changes) { - const state = backendState(backend) - const patch = state.applyChanges(changes) - backend.frozen = true - return [{state, heads: state.heads}, patch] -} - -function hashByActor(state, actorId, index) { - if (state.hashesByActor[actorId] && state.hashesByActor[actorId][index]) { - return state.hashesByActor[actorId][index] - } - if (!state.haveHashGraph) { - state.computeHashGraph() - if (state.hashesByActor[actorId] && state.hashesByActor[actorId][index]) { - return state.hashesByActor[actorId][index] - } - } - throw new RangeError(`Unknown change: actorId = ${actorId}, seq = ${index + 1}`) -} - -/** - * Takes a single change request `request` made by the local user, and applies - * it to the node state `backend`. Returns a three-element array `[backend, patch, binaryChange]` - * where `backend` is the updated node state,`patch` confirms the - * modifications to the document objects, and `binaryChange` is a binary-encoded form of - * the change submitted. - */ -function applyLocalChange(backend, change) { - const state = backendState(backend) - if (change.seq <= state.clock[change.actor] || 0) { - throw new RangeError('Change request has already been applied') - } - - // Add the local actor's last change hash to deps. We do this because when frontend - // and backend are on separate threads, the frontend may fire off several local - // changes in sequence before getting a response from the backend; since the binary - // encoding and hashing is done by the backend, the frontend does not know the hash - // of its own last change in this case. Rather than handle this situation as a - // special case, we say that the frontend includes only specifies other actors' - // deps in changes it generates, and the dependency from the local actor's last - // change is always added here in the backend. - // - // Strictly speaking, we should check whether the local actor's last change is - // indirectly reachable through a different actor's change; in that case, it is not - // necessary to add this dependency. However, it doesn't do any harm either (only - // using a few extra bytes of storage). - if (change.seq > 1) { - const lastHash = hashByActor(state, change.actor, change.seq - 2) - if (!lastHash) { - throw new RangeError(`Cannot find hash of localChange before seq=${change.seq}`) - } - let deps = {[lastHash]: true} - for (let hash of change.deps) deps[hash] = true - change.deps = Object.keys(deps).sort() - } - - const binaryChange = encodeChange(change) - const patch = state.applyChanges([binaryChange], true) - backend.frozen = true - - // On the patch we send out, omit the last local change hash - const lastHash = hashByActor(state, change.actor, change.seq - 1) - patch.deps = patch.deps.filter(head => head !== lastHash) - return [{state, heads: state.heads}, patch, binaryChange] -} - -/** - * Returns the state of the document serialised to an Uint8Array. - */ -function save(backend) { - return backendState(backend).save() -} - -/** - * Loads the document and/or changes contained in an Uint8Array, and returns a - * backend initialised with this state. - */ -function load(data) { - const state = new BackendDoc(data) - return {state, heads: state.heads} -} - -/** - * Applies a list of `changes` to the node state `backend`, and returns the updated - * state with those changes incorporated. Unlike `applyChanges()`, this function - * does not produce a patch describing the incremental modifications, making it - * a little faster when loading a document from disk. When all the changes have - * been loaded, you can use `getPatch()` to construct the latest document state. - */ -function loadChanges(backend, changes) { - const state = backendState(backend) - state.applyChanges(changes) - backend.frozen = true - return {state, heads: state.heads} -} - -/** - * Returns a patch that, when applied to an empty document, constructs the - * document tree in the state described by the node state `backend`. - */ -function getPatch(backend) { - return backendState(backend).getPatch() -} - -/** - * Returns an array of hashes of the current "head" changes (i.e. those changes - * that no other change depends on). - */ -function getHeads(backend) { - return backend.heads -} - -/** - * Returns the full history of changes that have been applied to a document. - */ -function getAllChanges(backend) { - return getChanges(backend, []) -} - -/** - * Returns all changes that are newer than or concurrent to the changes - * identified by the hashes in `haveDeps`. If `haveDeps` is an empty array, all - * changes are returned. Throws an exception if any of the given hashes is unknown. - */ -function getChanges(backend, haveDeps) { - if (!Array.isArray(haveDeps)) { - throw new TypeError('Pass an array of hashes to Backend.getChanges()') - } - return backendState(backend).getChanges(haveDeps) -} - -/** - * Returns all changes that are present in `backend2` but not in `backend1`. - * Intended for use in situations where the two backends are for different actors. - * To get the changes added between an older and a newer document state of the same - * actor, use `getChanges()` instead. `getChangesAdded()` throws an exception if - * one of the backend states is frozen (i.e. if it is not the latest state of that - * backend instance; this distinction matters when the backend is mutable). - */ -function getChangesAdded(backend1, backend2) { - return backendState(backend2).getChangesAdded(backendState(backend1)) -} - -/** - * If the backend has applied a change with the given `hash` (given as a - * hexadecimal string), returns that change (as a byte array). Returns undefined - * if no change with that hash has been applied. A change with missing - * dependencies does not count as having been applied. - */ -function getChangeByHash(backend, hash) { - return backendState(backend).getChangeByHash(hash) -} - -/** - * Returns the hashes of any missing dependencies, i.e. where we have applied a - * change that has a dependency on a change we have not seen. - * - * If the argument `heads` is given (an array of hexadecimal strings representing - * hashes as returned by `getHeads()`), this function also ensures that all of - * those hashes resolve to either a change that has been applied to the document, - * or that has been enqueued for later application once missing dependencies have - * arrived. Any missing heads hashes are included in the returned array. - */ -function getMissingDeps(backend, heads = []) { - return backendState(backend).getMissingDeps(heads) -} - -module.exports = { - init, clone, free, applyChanges, applyLocalChange, save, load, loadChanges, getPatch, - getHeads, getAllChanges, getChanges, getChangesAdded, getChangeByHash, getMissingDeps -} - -},{"./columnar":47,"./new":50,"./util":52}],47:[function(require,module,exports){ -const pako = require('pako') -const { copyObject, parseOpId, equalBytes } = require('../src/common') -const { - utf8ToString, hexStringToBytes, bytesToHexString, - Encoder, Decoder, RLEEncoder, RLEDecoder, DeltaEncoder, DeltaDecoder, BooleanEncoder, BooleanDecoder -} = require('./encoding') - -// Maybe we should be using the platform's built-in hash implementation? -// Node has the crypto module: https://nodejs.org/api/crypto.html and browsers have -// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest -// However, the WebCrypto API is asynchronous (returns promises), which would -// force all our APIs to become asynchronous as well, which would be annoying. -// -// I think on balance, it's safe enough to use a random library off npm: -// - We only need one hash function (not a full suite of crypto algorithms); -// - SHA256 is quite simple and has fairly few opportunities for subtle bugs -// (compared to asymmetric cryptography anyway); -// - It does not need a secure source of random bits and does not need to be -// constant-time; -// - I have reviewed the source code and it seems pretty reasonable. -const { Hash } = require('fast-sha256') - -// These bytes don't mean anything, they were generated randomly -const MAGIC_BYTES = new Uint8Array([0x85, 0x6f, 0x4a, 0x83]) - -const CHUNK_TYPE_DOCUMENT = 0 -const CHUNK_TYPE_CHANGE = 1 -const CHUNK_TYPE_DEFLATE = 2 // like CHUNK_TYPE_CHANGE but with DEFLATE compression - -// Minimum number of bytes in a value before we enable DEFLATE compression (there is no point -// compressing very short values since compression may actually make them bigger) -const DEFLATE_MIN_SIZE = 256 - -// The least-significant 3 bits of a columnId indicate its datatype -const COLUMN_TYPE = { - GROUP_CARD: 0, ACTOR_ID: 1, INT_RLE: 2, INT_DELTA: 3, BOOLEAN: 4, - STRING_RLE: 5, VALUE_LEN: 6, VALUE_RAW: 7 -} - -// The 4th-least-significant bit of a columnId is set if the column is DEFLATE-compressed -const COLUMN_TYPE_DEFLATE = 8 - -// In the values in a column of type VALUE_LEN, the bottom four bits indicate the type of the value, -// one of the following types in VALUE_TYPE. The higher bits indicate the length of the value in the -// associated VALUE_RAW column (in bytes). -const VALUE_TYPE = { - NULL: 0, FALSE: 1, TRUE: 2, LEB128_UINT: 3, LEB128_INT: 4, IEEE754: 5, - UTF8: 6, BYTES: 7, COUNTER: 8, TIMESTAMP: 9, MIN_UNKNOWN: 10, MAX_UNKNOWN: 15 -} - -// make* actions must be at even-numbered indexes in this list -const ACTIONS = ['makeMap', 'set', 'makeList', 'del', 'makeText', 'inc', 'makeTable', 'link'] - -const OBJECT_TYPE = {makeMap: 'map', makeList: 'list', makeText: 'text', makeTable: 'table'} - -const COMMON_COLUMNS = [ - {columnName: 'objActor', columnId: 0 << 4 | COLUMN_TYPE.ACTOR_ID}, - {columnName: 'objCtr', columnId: 0 << 4 | COLUMN_TYPE.INT_RLE}, - {columnName: 'keyActor', columnId: 1 << 4 | COLUMN_TYPE.ACTOR_ID}, - {columnName: 'keyCtr', columnId: 1 << 4 | COLUMN_TYPE.INT_DELTA}, - {columnName: 'keyStr', columnId: 1 << 4 | COLUMN_TYPE.STRING_RLE}, - {columnName: 'idActor', columnId: 2 << 4 | COLUMN_TYPE.ACTOR_ID}, - {columnName: 'idCtr', columnId: 2 << 4 | COLUMN_TYPE.INT_DELTA}, - {columnName: 'insert', columnId: 3 << 4 | COLUMN_TYPE.BOOLEAN}, - {columnName: 'action', columnId: 4 << 4 | COLUMN_TYPE.INT_RLE}, - {columnName: 'valLen', columnId: 5 << 4 | COLUMN_TYPE.VALUE_LEN}, - {columnName: 'valRaw', columnId: 5 << 4 | COLUMN_TYPE.VALUE_RAW}, - {columnName: 'chldActor', columnId: 6 << 4 | COLUMN_TYPE.ACTOR_ID}, - {columnName: 'chldCtr', columnId: 6 << 4 | COLUMN_TYPE.INT_DELTA} -] - -const CHANGE_COLUMNS = COMMON_COLUMNS.concat([ - {columnName: 'predNum', columnId: 7 << 4 | COLUMN_TYPE.GROUP_CARD}, - {columnName: 'predActor', columnId: 7 << 4 | COLUMN_TYPE.ACTOR_ID}, - {columnName: 'predCtr', columnId: 7 << 4 | COLUMN_TYPE.INT_DELTA} -]) - -const DOC_OPS_COLUMNS = COMMON_COLUMNS.concat([ - {columnName: 'succNum', columnId: 8 << 4 | COLUMN_TYPE.GROUP_CARD}, - {columnName: 'succActor', columnId: 8 << 4 | COLUMN_TYPE.ACTOR_ID}, - {columnName: 'succCtr', columnId: 8 << 4 | COLUMN_TYPE.INT_DELTA} -]) - -const DOCUMENT_COLUMNS = [ - {columnName: 'actor', columnId: 0 << 4 | COLUMN_TYPE.ACTOR_ID}, - {columnName: 'seq', columnId: 0 << 4 | COLUMN_TYPE.INT_DELTA}, - {columnName: 'maxOp', columnId: 1 << 4 | COLUMN_TYPE.INT_DELTA}, - {columnName: 'time', columnId: 2 << 4 | COLUMN_TYPE.INT_DELTA}, - {columnName: 'message', columnId: 3 << 4 | COLUMN_TYPE.STRING_RLE}, - {columnName: 'depsNum', columnId: 4 << 4 | COLUMN_TYPE.GROUP_CARD}, - {columnName: 'depsIndex', columnId: 4 << 4 | COLUMN_TYPE.INT_DELTA}, - {columnName: 'extraLen', columnId: 5 << 4 | COLUMN_TYPE.VALUE_LEN}, - {columnName: 'extraRaw', columnId: 5 << 4 | COLUMN_TYPE.VALUE_RAW} -] - -/** - * Maps an opId of the form {counter: 12345, actorId: 'someActorId'} to the form - * {counter: 12345, actorNum: 123, actorId: 'someActorId'}, where the actorNum - * is the index into the `actorIds` array. - */ -function actorIdToActorNum(opId, actorIds) { - if (!opId || !opId.actorId) return opId - const counter = opId.counter - const actorNum = actorIds.indexOf(opId.actorId) - if (actorNum < 0) throw new RangeError('missing actorId') // should not happen - return {counter, actorNum, actorId: opId.actorId} -} - -/** - * Comparison function to pass to Array.sort(), which compares two opIds in the - * form produced by `actorIdToActorNum` so that they are sorted in increasing - * Lamport timestamp order (sorted first by counter, then by actorId). - */ -function compareParsedOpIds(id1, id2) { - if (id1.counter < id2.counter) return -1 - if (id1.counter > id2.counter) return +1 - if (id1.actorId < id2.actorId) return -1 - if (id1.actorId > id2.actorId) return +1 - return 0 -} - -/** - * Takes `changes`, an array of changes (represented as JS objects). Returns an - * object `{changes, actorIds}`, where `changes` is a copy of the argument in - * which all string opIds have been replaced with `{counter, actorNum}` objects, - * and where `actorIds` is a lexicographically sorted array of actor IDs occurring - * in any of the operations. `actorNum` is an index into that array of actorIds. - * If `single` is true, the actorId of the author of the change is moved to the - * beginning of the array of actorIds, so that `actorNum` is zero when referencing - * the author of the change itself. This special-casing is omitted if `single` is - * false. - */ -function parseAllOpIds(changes, single) { - const actors = {}, newChanges = [] - for (let change of changes) { - change = copyObject(change) - actors[change.actor] = true - change.ops = expandMultiOps(change.ops, change.startOp, change.actor) - change.ops = change.ops.map(op => { - op = copyObject(op) - if (op.obj !== '_root') op.obj = parseOpId(op.obj) - if (op.elemId && op.elemId !== '_head') op.elemId = parseOpId(op.elemId) - if (op.child) op.child = parseOpId(op.child) - if (op.pred) op.pred = op.pred.map(parseOpId) - if (op.obj.actorId) actors[op.obj.actorId] = true - if (op.elemId && op.elemId.actorId) actors[op.elemId.actorId] = true - if (op.child && op.child.actorId) actors[op.child.actorId] = true - for (let pred of op.pred) actors[pred.actorId] = true - return op - }) - newChanges.push(change) - } - - let actorIds = Object.keys(actors).sort() - if (single) { - actorIds = [changes[0].actor].concat(actorIds.filter(actor => actor !== changes[0].actor)) - } - for (let change of newChanges) { - change.actorNum = actorIds.indexOf(change.actor) - for (let i = 0; i < change.ops.length; i++) { - let op = change.ops[i] - op.id = {counter: change.startOp + i, actorNum: change.actorNum, actorId: change.actor} - op.obj = actorIdToActorNum(op.obj, actorIds) - op.elemId = actorIdToActorNum(op.elemId, actorIds) - op.child = actorIdToActorNum(op.child, actorIds) - op.pred = op.pred.map(pred => actorIdToActorNum(pred, actorIds)) - } - } - return {changes: newChanges, actorIds} -} - -/** - * Encodes the `obj` property of operation `op` into the two columns - * `objActor` and `objCtr`. - */ -function encodeObjectId(op, columns) { - if (op.obj === '_root') { - columns.objActor.appendValue(null) - columns.objCtr.appendValue(null) - } else if (op.obj.actorNum >= 0 && op.obj.counter > 0) { - columns.objActor.appendValue(op.obj.actorNum) - columns.objCtr.appendValue(op.obj.counter) - } else { - throw new RangeError(`Unexpected objectId reference: ${JSON.stringify(op.obj)}`) - } -} - -/** - * Encodes the `key` and `elemId` properties of operation `op` into the three - * columns `keyActor`, `keyCtr`, and `keyStr`. - */ -function encodeOperationKey(op, columns) { - if (op.key) { - columns.keyActor.appendValue(null) - columns.keyCtr.appendValue(null) - columns.keyStr.appendValue(op.key) - } else if (op.elemId === '_head' && op.insert) { - columns.keyActor.appendValue(null) - columns.keyCtr.appendValue(0) - columns.keyStr.appendValue(null) - } else if (op.elemId && op.elemId.actorNum >= 0 && op.elemId.counter > 0) { - columns.keyActor.appendValue(op.elemId.actorNum) - columns.keyCtr.appendValue(op.elemId.counter) - columns.keyStr.appendValue(null) - } else { - throw new RangeError(`Unexpected operation key: ${JSON.stringify(op)}`) - } -} - -/** - * Encodes the `action` property of operation `op` into the `action` column. - */ -function encodeOperationAction(op, columns) { - const actionCode = ACTIONS.indexOf(op.action) - if (actionCode >= 0) { - columns.action.appendValue(actionCode) - } else if (typeof op.action === 'number') { - columns.action.appendValue(op.action) - } else { - throw new RangeError(`Unexpected operation action: ${op.action}`) - } -} - -/** - * Given the datatype for a number, determine the typeTag and the value to encode - * otherwise guess - */ -function getNumberTypeAndValue(op) { - switch (op.datatype) { - case "counter": - return [ VALUE_TYPE.COUNTER, op.value ] - case "timestamp": - return [ VALUE_TYPE.TIMESTAMP, op.value ] - case "uint": - return [ VALUE_TYPE.LEB128_UINT, op.value ] - case "int": - return [ VALUE_TYPE.LEB128_INT, op.value ] - case "float64": { - const buf64 = new ArrayBuffer(8), view64 = new DataView(buf64) - view64.setFloat64(0, op.value, true) - return [ VALUE_TYPE.IEEE754, new Uint8Array(buf64) ] - } - default: - // increment operators get resolved here ... - if (Number.isInteger(op.value) && op.value <= Number.MAX_SAFE_INTEGER && op.value >= Number.MIN_SAFE_INTEGER) { - return [ VALUE_TYPE.LEB128_INT, op.value ] - } else { - const buf64 = new ArrayBuffer(8), view64 = new DataView(buf64) - view64.setFloat64(0, op.value, true) - return [ VALUE_TYPE.IEEE754, new Uint8Array(buf64) ] - } - } -} - -/** - * Encodes the `value` property of operation `op` into the two columns - * `valLen` and `valRaw`. - */ -function encodeValue(op, columns) { - if ((op.action !== 'set' && op.action !== 'inc') || op.value === null) { - columns.valLen.appendValue(VALUE_TYPE.NULL) - } else if (op.value === false) { - columns.valLen.appendValue(VALUE_TYPE.FALSE) - } else if (op.value === true) { - columns.valLen.appendValue(VALUE_TYPE.TRUE) - } else if (typeof op.value === 'string') { - const numBytes = columns.valRaw.appendRawString(op.value) - columns.valLen.appendValue(numBytes << 4 | VALUE_TYPE.UTF8) - } else if (ArrayBuffer.isView(op.value)) { - const numBytes = columns.valRaw.appendRawBytes(new Uint8Array(op.value.buffer)) - columns.valLen.appendValue(numBytes << 4 | VALUE_TYPE.BYTES) - } else if (typeof op.value === 'number') { - let [typeTag, value] = getNumberTypeAndValue(op) - let numBytes - if (typeTag === VALUE_TYPE.LEB128_UINT) { - numBytes = columns.valRaw.appendUint53(value) - } else if (typeTag === VALUE_TYPE.IEEE754) { - numBytes = columns.valRaw.appendRawBytes(value) - } else { - numBytes = columns.valRaw.appendInt53(value) - } - columns.valLen.appendValue(numBytes << 4 | typeTag) - } else if (typeof op.datatype === 'number' && op.datatype >= VALUE_TYPE.MIN_UNKNOWN && - op.datatype <= VALUE_TYPE.MAX_UNKNOWN && op.value instanceof Uint8Array) { - const numBytes = columns.valRaw.appendRawBytes(op.value) - columns.valLen.appendValue(numBytes << 4 | op.datatype) - } else if (op.datatype) { - throw new RangeError(`Unknown datatype ${op.datatype} for value ${op.value}`) - } else { - throw new RangeError(`Unsupported value in operation: ${op.value}`) - } -} - -/** - * Given `sizeTag` (an unsigned integer read from a VALUE_LEN column) and `bytes` (a Uint8Array - * read from a VALUE_RAW column, with length `sizeTag >> 4`), this function returns an object of the - * form `{value: value, datatype: datatypeTag}` where `value` is a JavaScript primitive datatype - * corresponding to the value, and `datatypeTag` is a datatype annotation such as 'counter'. - */ -function decodeValue(sizeTag, bytes) { - if (sizeTag === VALUE_TYPE.NULL) { - return {value: null} - } else if (sizeTag === VALUE_TYPE.FALSE) { - return {value: false} - } else if (sizeTag === VALUE_TYPE.TRUE) { - return {value: true} - } else if (sizeTag % 16 === VALUE_TYPE.UTF8) { - return {value: utf8ToString(bytes)} - } else { - if (sizeTag % 16 === VALUE_TYPE.LEB128_UINT) { - return {value: new Decoder(bytes).readUint53(), datatype: "uint"} - } else if (sizeTag % 16 === VALUE_TYPE.LEB128_INT) { - return {value: new Decoder(bytes).readInt53(), datatype: "int"} - } else if (sizeTag % 16 === VALUE_TYPE.IEEE754) { - const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength) - if (bytes.byteLength === 8) { - return {value: view.getFloat64(0, true), datatype: "float64"} - } else { - throw new RangeError(`Invalid length for floating point number: ${bytes.byteLength}`) - } - } else if (sizeTag % 16 === VALUE_TYPE.COUNTER) { - return {value: new Decoder(bytes).readInt53(), datatype: 'counter'} - } else if (sizeTag % 16 === VALUE_TYPE.TIMESTAMP) { - return {value: new Decoder(bytes).readInt53(), datatype: 'timestamp'} - } else { - return {value: bytes, datatype: sizeTag % 16} - } - } -} - -/** - * Reads one value from the column `columns[colIndex]` and interprets it based - * on the column type. `actorIds` is a list of actors that appear in the change; - * `actorIds[0]` is the actorId of the change's author. Mutates the `result` - * object with the value, and returns the number of columns processed (this is 2 - * in the case of a pair of VALUE_LEN and VALUE_RAW columns, which are processed - * in one go). - */ -function decodeValueColumns(columns, colIndex, actorIds, result) { - const { columnId, columnName, decoder } = columns[colIndex] - if (columnId % 8 === COLUMN_TYPE.VALUE_LEN && colIndex + 1 < columns.length && - columns[colIndex + 1].columnId === columnId + 1) { - const sizeTag = decoder.readValue() - const rawValue = columns[colIndex + 1].decoder.readRawBytes(sizeTag >> 4) - const { value, datatype } = decodeValue(sizeTag, rawValue) - result[columnName] = value - if (datatype) result[columnName + '_datatype'] = datatype - return 2 - } else if (columnId % 8 === COLUMN_TYPE.ACTOR_ID) { - const actorNum = decoder.readValue() - if (actorNum === null) { - result[columnName] = null - } else { - if (!actorIds[actorNum]) throw new RangeError(`No actor index ${actorNum}`) - result[columnName] = actorIds[actorNum] - } - } else { - result[columnName] = decoder.readValue() - } - return 1 -} - -/** - * Encodes an array of operations in a set of columns. The operations need to - * be parsed with `parseAllOpIds()` beforehand. If `forDocument` is true, we use - * the column structure of a whole document, otherwise we use the column - * structure for an individual change. Returns an array of - * `{columnId, columnName, encoder}` objects. - */ -function encodeOps(ops, forDocument) { - const columns = { - objActor : new RLEEncoder('uint'), - objCtr : new RLEEncoder('uint'), - keyActor : new RLEEncoder('uint'), - keyCtr : new DeltaEncoder(), - keyStr : new RLEEncoder('utf8'), - insert : new BooleanEncoder(), - action : new RLEEncoder('uint'), - valLen : new RLEEncoder('uint'), - valRaw : new Encoder(), - chldActor : new RLEEncoder('uint'), - chldCtr : new DeltaEncoder() - } - - if (forDocument) { - columns.idActor = new RLEEncoder('uint') - columns.idCtr = new DeltaEncoder() - columns.succNum = new RLEEncoder('uint') - columns.succActor = new RLEEncoder('uint') - columns.succCtr = new DeltaEncoder() - } else { - columns.predNum = new RLEEncoder('uint') - columns.predCtr = new DeltaEncoder() - columns.predActor = new RLEEncoder('uint') - } - - for (let op of ops) { - encodeObjectId(op, columns) - encodeOperationKey(op, columns) - columns.insert.appendValue(!!op.insert) - encodeOperationAction(op, columns) - encodeValue(op, columns) - - if (op.child && op.child.counter) { - columns.chldActor.appendValue(op.child.actorNum) - columns.chldCtr.appendValue(op.child.counter) - } else { - columns.chldActor.appendValue(null) - columns.chldCtr.appendValue(null) - } - - if (forDocument) { - columns.idActor.appendValue(op.id.actorNum) - columns.idCtr.appendValue(op.id.counter) - columns.succNum.appendValue(op.succ.length) - op.succ.sort(compareParsedOpIds) - for (let i = 0; i < op.succ.length; i++) { - columns.succActor.appendValue(op.succ[i].actorNum) - columns.succCtr.appendValue(op.succ[i].counter) - } - } else { - columns.predNum.appendValue(op.pred.length) - op.pred.sort(compareParsedOpIds) - for (let i = 0; i < op.pred.length; i++) { - columns.predActor.appendValue(op.pred[i].actorNum) - columns.predCtr.appendValue(op.pred[i].counter) - } - } - } - - let columnList = [] - for (let {columnName, columnId} of forDocument ? DOC_OPS_COLUMNS : CHANGE_COLUMNS) { - if (columns[columnName]) columnList.push({columnId, columnName, encoder: columns[columnName]}) - } - return columnList.sort((a, b) => a.columnId - b.columnId) -} - -function validDatatype(value, datatype) { - if (datatype === undefined) { - return (typeof value === 'string' || typeof value === 'boolean' || value === null) - } else { - return typeof value === 'number' - } -} - -function expandMultiOps(ops, startOp, actor) { - let opNum = startOp - let expandedOps = [] - for (const op of ops) { - if (op.action === 'set' && op.values && op.insert) { - if (op.pred.length !== 0) throw new RangeError('multi-insert pred must be empty') - let lastElemId = op.elemId - const datatype = op.datatype - for (const value of op.values) { - if (!validDatatype(value, datatype)) throw new RangeError(`Decode failed: bad value/datatype association (${value},${datatype})`) - expandedOps.push({action: 'set', obj: op.obj, elemId: lastElemId, datatype, value, pred: [], insert: true}) - lastElemId = `${opNum}@${actor}` - opNum += 1 - } - } else if (op.action === 'del' && op.multiOp > 1) { - if (op.pred.length !== 1) throw new RangeError('multiOp deletion must have exactly one pred') - const startElemId = parseOpId(op.elemId), startPred = parseOpId(op.pred[0]) - for (let i = 0; i < op.multiOp; i++) { - const elemId = `${startElemId.counter + i}@${startElemId.actorId}` - const pred = [`${startPred.counter + i}@${startPred.actorId}`] - expandedOps.push({action: 'del', obj: op.obj, elemId, pred}) - opNum += 1 - } - } else { - expandedOps.push(op) - opNum += 1 - } - } - return expandedOps -} - -/** - * Takes a change as decoded by `decodeColumns`, and changes it into the form - * expected by the rest of the backend. If `forDocument` is true, we use the op - * structure of a whole document, otherwise we use the op structure for an - * individual change. - */ -function decodeOps(ops, forDocument) { - const newOps = [] - for (let op of ops) { - const obj = (op.objCtr === null) ? '_root' : `${op.objCtr}@${op.objActor}` - const elemId = op.keyStr ? undefined : (op.keyCtr === 0 ? '_head' : `${op.keyCtr}@${op.keyActor}`) - const action = ACTIONS[op.action] || op.action - const newOp = elemId ? {obj, elemId, action} : {obj, key: op.keyStr, action} - newOp.insert = !!op.insert - if (ACTIONS[op.action] === 'set' || ACTIONS[op.action] === 'inc') { - newOp.value = op.valLen - if (op.valLen_datatype) newOp.datatype = op.valLen_datatype - } - if (!!op.chldCtr !== !!op.chldActor) { - throw new RangeError(`Mismatched child columns: ${op.chldCtr} and ${op.chldActor}`) - } - if (op.chldCtr !== null) newOp.child = `${op.chldCtr}@${op.chldActor}` - if (forDocument) { - newOp.id = `${op.idCtr}@${op.idActor}` - newOp.succ = op.succNum.map(succ => `${succ.succCtr}@${succ.succActor}`) - checkSortedOpIds(op.succNum.map(succ => ({counter: succ.succCtr, actorId: succ.succActor}))) - } else { - newOp.pred = op.predNum.map(pred => `${pred.predCtr}@${pred.predActor}`) - checkSortedOpIds(op.predNum.map(pred => ({counter: pred.predCtr, actorId: pred.predActor}))) - } - newOps.push(newOp) - } - return newOps -} - -/** - * Throws an exception if the opIds in the given array are not in sorted order. - */ -function checkSortedOpIds(opIds) { - let last = null - for (let opId of opIds) { - if (last && compareParsedOpIds(last, opId) !== -1) { - throw new RangeError('operation IDs are not in ascending order') - } - last = opId - } -} - -function encoderByColumnId(columnId) { - if ((columnId & 7) === COLUMN_TYPE.INT_DELTA) { - return new DeltaEncoder() - } else if ((columnId & 7) === COLUMN_TYPE.BOOLEAN) { - return new BooleanEncoder() - } else if ((columnId & 7) === COLUMN_TYPE.STRING_RLE) { - return new RLEEncoder('utf8') - } else if ((columnId & 7) === COLUMN_TYPE.VALUE_RAW) { - return new Encoder() - } else { - return new RLEEncoder('uint') - } -} - -function decoderByColumnId(columnId, buffer) { - if ((columnId & 7) === COLUMN_TYPE.INT_DELTA) { - return new DeltaDecoder(buffer) - } else if ((columnId & 7) === COLUMN_TYPE.BOOLEAN) { - return new BooleanDecoder(buffer) - } else if ((columnId & 7) === COLUMN_TYPE.STRING_RLE) { - return new RLEDecoder('utf8', buffer) - } else if ((columnId & 7) === COLUMN_TYPE.VALUE_RAW) { - return new Decoder(buffer) - } else { - return new RLEDecoder('uint', buffer) - } -} - -function makeDecoders(columns, columnSpec) { - const emptyBuf = new Uint8Array(0) - let decoders = [], columnIndex = 0, specIndex = 0 - - while (columnIndex < columns.length || specIndex < columnSpec.length) { - if (columnIndex === columns.length || - (specIndex < columnSpec.length && columnSpec[specIndex].columnId < columns[columnIndex].columnId)) { - const {columnId, columnName} = columnSpec[specIndex] - decoders.push({columnId, columnName, decoder: decoderByColumnId(columnId, emptyBuf)}) - specIndex++ - } else if (specIndex === columnSpec.length || columns[columnIndex].columnId < columnSpec[specIndex].columnId) { - const {columnId, buffer} = columns[columnIndex] - decoders.push({columnId, decoder: decoderByColumnId(columnId, buffer)}) - columnIndex++ - } else { // columns[columnIndex].columnId === columnSpec[specIndex].columnId - const {columnId, buffer} = columns[columnIndex], {columnName} = columnSpec[specIndex] - decoders.push({columnId, columnName, decoder: decoderByColumnId(columnId, buffer)}) - columnIndex++ - specIndex++ - } - } - return decoders -} - -function decodeColumns(columns, actorIds, columnSpec) { - columns = makeDecoders(columns, columnSpec) - let parsedRows = [] - while (columns.some(col => !col.decoder.done)) { - let row = {}, col = 0 - while (col < columns.length) { - const columnId = columns[col].columnId - let groupId = columnId >> 4, groupCols = 1 - while (col + groupCols < columns.length && columns[col + groupCols].columnId >> 4 === groupId) { - groupCols++ - } - - if (columnId % 8 === COLUMN_TYPE.GROUP_CARD) { - const values = [], count = columns[col].decoder.readValue() - for (let i = 0; i < count; i++) { - let value = {} - for (let colOffset = 1; colOffset < groupCols; colOffset++) { - decodeValueColumns(columns, col + colOffset, actorIds, value) - } - values.push(value) - } - row[columns[col].columnName] = values - col += groupCols - } else { - col += decodeValueColumns(columns, col, actorIds, row) - } - } - parsedRows.push(row) - } - return parsedRows -} - -function decodeColumnInfo(decoder) { - // A number that is all 1 bits except for the bit that indicates whether a column is - // deflate-compressed. We ignore this bit when checking whether columns are sorted by ID. - const COLUMN_ID_MASK = (-1 ^ COLUMN_TYPE_DEFLATE) >>> 0 - - let lastColumnId = -1, columns = [], numColumns = decoder.readUint53() - for (let i = 0; i < numColumns; i++) { - const columnId = decoder.readUint53(), bufferLen = decoder.readUint53() - if ((columnId & COLUMN_ID_MASK) <= (lastColumnId & COLUMN_ID_MASK)) { - throw new RangeError('Columns must be in ascending order') - } - lastColumnId = columnId - columns.push({columnId, bufferLen}) - } - return columns -} - -function encodeColumnInfo(encoder, columns) { - const nonEmptyColumns = columns.filter(column => column.encoder.buffer.byteLength > 0) - encoder.appendUint53(nonEmptyColumns.length) - for (let column of nonEmptyColumns) { - encoder.appendUint53(column.columnId) - encoder.appendUint53(column.encoder.buffer.byteLength) - } -} - -function decodeChangeHeader(decoder) { - const numDeps = decoder.readUint53(), deps = [] - for (let i = 0; i < numDeps; i++) { - deps.push(bytesToHexString(decoder.readRawBytes(32))) - } - let change = { - actor: decoder.readHexString(), - seq: decoder.readUint53(), - startOp: decoder.readUint53(), - time: decoder.readInt53(), - message: decoder.readPrefixedString(), - deps - } - const actorIds = [change.actor], numActorIds = decoder.readUint53() - for (let i = 0; i < numActorIds; i++) actorIds.push(decoder.readHexString()) - change.actorIds = actorIds - return change -} - -/** - * Assembles a chunk of encoded data containing a checksum, headers, and a - * series of encoded columns. Calls `encodeHeaderCallback` with an encoder that - * should be used to add the headers. The columns should be given as `columns`. - */ -function encodeContainer(chunkType, encodeContentsCallback) { - const CHECKSUM_SIZE = 4 // checksum is first 4 bytes of SHA-256 hash of the rest of the data - const HEADER_SPACE = MAGIC_BYTES.byteLength + CHECKSUM_SIZE + 1 + 5 // 1 byte type + 5 bytes length - const body = new Encoder() - // Make space for the header at the beginning of the body buffer. We will - // copy the header in here later. This is cheaper than copying the body since - // the body is likely to be much larger than the header. - body.appendRawBytes(new Uint8Array(HEADER_SPACE)) - encodeContentsCallback(body) - - const bodyBuf = body.buffer - const header = new Encoder() - header.appendByte(chunkType) - header.appendUint53(bodyBuf.byteLength - HEADER_SPACE) - - // Compute the hash over chunkType, length, and body - const headerBuf = header.buffer - const sha256 = new Hash() - sha256.update(headerBuf) - sha256.update(bodyBuf.subarray(HEADER_SPACE)) - const hash = sha256.digest(), checksum = hash.subarray(0, CHECKSUM_SIZE) - - // Copy header into the body buffer so that they are contiguous - bodyBuf.set(MAGIC_BYTES, HEADER_SPACE - headerBuf.byteLength - CHECKSUM_SIZE - MAGIC_BYTES.byteLength) - bodyBuf.set(checksum, HEADER_SPACE - headerBuf.byteLength - CHECKSUM_SIZE) - bodyBuf.set(headerBuf, HEADER_SPACE - headerBuf.byteLength) - return {hash, bytes: bodyBuf.subarray(HEADER_SPACE - headerBuf.byteLength - CHECKSUM_SIZE - MAGIC_BYTES.byteLength)} -} - -function decodeContainerHeader(decoder, computeHash) { - if (!equalBytes(decoder.readRawBytes(MAGIC_BYTES.byteLength), MAGIC_BYTES)) { - throw new RangeError('Data does not begin with magic bytes 85 6f 4a 83') - } - const expectedHash = decoder.readRawBytes(4) - const hashStartOffset = decoder.offset - const chunkType = decoder.readByte() - const chunkLength = decoder.readUint53() - const header = {chunkType, chunkLength, chunkData: decoder.readRawBytes(chunkLength)} - - if (computeHash) { - const sha256 = new Hash() - sha256.update(decoder.buf.subarray(hashStartOffset, decoder.offset)) - const binaryHash = sha256.digest() - if (!equalBytes(binaryHash.subarray(0, 4), expectedHash)) { - throw new RangeError('checksum does not match data') - } - header.hash = bytesToHexString(binaryHash) - } - return header -} - -function encodeChange(changeObj) { - const { changes, actorIds } = parseAllOpIds([changeObj], true) - const change = changes[0] - - const { hash, bytes } = encodeContainer(CHUNK_TYPE_CHANGE, encoder => { - if (!Array.isArray(change.deps)) throw new TypeError('deps is not an array') - encoder.appendUint53(change.deps.length) - for (let hash of change.deps.slice().sort()) { - encoder.appendRawBytes(hexStringToBytes(hash)) - } - encoder.appendHexString(change.actor) - encoder.appendUint53(change.seq) - encoder.appendUint53(change.startOp) - encoder.appendInt53(change.time) - encoder.appendPrefixedString(change.message || '') - encoder.appendUint53(actorIds.length - 1) - for (let actor of actorIds.slice(1)) encoder.appendHexString(actor) - - const columns = encodeOps(change.ops, false) - encodeColumnInfo(encoder, columns) - for (let column of columns) encoder.appendRawBytes(column.encoder.buffer) - if (change.extraBytes) encoder.appendRawBytes(change.extraBytes) - }) - - const hexHash = bytesToHexString(hash) - if (changeObj.hash && changeObj.hash !== hexHash) { - throw new RangeError(`Change hash does not match encoding: ${changeObj.hash} != ${hexHash}`) - } - return (bytes.byteLength >= DEFLATE_MIN_SIZE) ? deflateChange(bytes) : bytes -} - -function decodeChangeColumns(buffer) { - if (buffer[8] === CHUNK_TYPE_DEFLATE) buffer = inflateChange(buffer) - const decoder = new Decoder(buffer) - const header = decodeContainerHeader(decoder, true) - const chunkDecoder = new Decoder(header.chunkData) - if (!decoder.done) throw new RangeError('Encoded change has trailing data') - if (header.chunkType !== CHUNK_TYPE_CHANGE) throw new RangeError(`Unexpected chunk type: ${header.chunkType}`) - - const change = decodeChangeHeader(chunkDecoder) - const columns = decodeColumnInfo(chunkDecoder) - for (let i = 0; i < columns.length; i++) { - if ((columns[i].columnId & COLUMN_TYPE_DEFLATE) !== 0) { - throw new RangeError('change must not contain deflated columns') - } - columns[i].buffer = chunkDecoder.readRawBytes(columns[i].bufferLen) - } - if (!chunkDecoder.done) { - const restLen = chunkDecoder.buf.byteLength - chunkDecoder.offset - change.extraBytes = chunkDecoder.readRawBytes(restLen) - } - - change.columns = columns - change.hash = header.hash - return change -} - -/** - * Decodes one change in binary format into its JS object representation. - */ -function decodeChange(buffer) { - const change = decodeChangeColumns(buffer) - change.ops = decodeOps(decodeColumns(change.columns, change.actorIds, CHANGE_COLUMNS), false) - delete change.actorIds - delete change.columns - return change -} - -/** - * Decodes the header fields of a change in binary format, but does not decode - * the operations. Saves work when we only need to inspect the headers. Only - * computes the hash of the change if `computeHash` is true. - */ -function decodeChangeMeta(buffer, computeHash) { - if (buffer[8] === CHUNK_TYPE_DEFLATE) buffer = inflateChange(buffer) - const header = decodeContainerHeader(new Decoder(buffer), computeHash) - if (header.chunkType !== CHUNK_TYPE_CHANGE) { - throw new RangeError('Buffer chunk type is not a change') - } - const meta = decodeChangeHeader(new Decoder(header.chunkData)) - meta.change = buffer - if (computeHash) meta.hash = header.hash - return meta -} - -/** - * Compresses a binary change using DEFLATE. - */ -function deflateChange(buffer) { - const header = decodeContainerHeader(new Decoder(buffer), false) - if (header.chunkType !== CHUNK_TYPE_CHANGE) throw new RangeError(`Unexpected chunk type: ${header.chunkType}`) - const compressed = pako.deflateRaw(header.chunkData) - const encoder = new Encoder() - encoder.appendRawBytes(buffer.subarray(0, 8)) // copy MAGIC_BYTES and checksum - encoder.appendByte(CHUNK_TYPE_DEFLATE) - encoder.appendUint53(compressed.byteLength) - encoder.appendRawBytes(compressed) - return encoder.buffer -} - -/** - * Decompresses a binary change that has been compressed with DEFLATE. - */ -function inflateChange(buffer) { - const header = decodeContainerHeader(new Decoder(buffer), false) - if (header.chunkType !== CHUNK_TYPE_DEFLATE) throw new RangeError(`Unexpected chunk type: ${header.chunkType}`) - const decompressed = pako.inflateRaw(header.chunkData) - const encoder = new Encoder() - encoder.appendRawBytes(buffer.subarray(0, 8)) // copy MAGIC_BYTES and checksum - encoder.appendByte(CHUNK_TYPE_CHANGE) - encoder.appendUint53(decompressed.byteLength) - encoder.appendRawBytes(decompressed) - return encoder.buffer -} - -/** - * Takes an Uint8Array that may contain multiple concatenated changes, and - * returns an array of subarrays, each subarray containing one change. - */ -function splitContainers(buffer) { - let decoder = new Decoder(buffer), chunks = [], startOffset = 0 - while (!decoder.done) { - decodeContainerHeader(decoder, false) - chunks.push(buffer.subarray(startOffset, decoder.offset)) - startOffset = decoder.offset - } - return chunks -} - -/** - * Decodes a list of changes from the binary format into JS objects. - * `binaryChanges` is an array of `Uint8Array` objects. - */ -function decodeChanges(binaryChanges) { - let decoded = [] - for (let binaryChange of binaryChanges) { - for (let chunk of splitContainers(binaryChange)) { - if (chunk[8] === CHUNK_TYPE_DOCUMENT) { - decoded = decoded.concat(decodeDocument(chunk)) - } else if (chunk[8] === CHUNK_TYPE_CHANGE || chunk[8] === CHUNK_TYPE_DEFLATE) { - decoded.push(decodeChange(chunk)) - } else { - // ignoring chunk of unknown type - } - } - } - return decoded -} - -function sortOpIds(a, b) { - if (a === b) return 0 - if (a === '_root') return -1 - if (b === '_root') return +1 - const a_ = parseOpId(a), b_ = parseOpId(b) - if (a_.counter < b_.counter) return -1 - if (a_.counter > b_.counter) return +1 - if (a_.actorId < b_.actorId) return -1 - if (a_.actorId > b_.actorId) return +1 - return 0 -} - -/** - * Takes a set of operations `ops` loaded from an encoded document, and - * reconstructs the changes that they originally came from. - * Does not return anything, only mutates `changes`. - */ -function groupChangeOps(changes, ops) { - let changesByActor = {} // map from actorId to array of changes by that actor - for (let change of changes) { - change.ops = [] - if (!changesByActor[change.actor]) changesByActor[change.actor] = [] - if (change.seq !== changesByActor[change.actor].length + 1) { - throw new RangeError(`Expected seq = ${changesByActor[change.actor].length + 1}, got ${change.seq}`) - } - if (change.seq > 1 && changesByActor[change.actor][change.seq - 2].maxOp > change.maxOp) { - throw new RangeError('maxOp must increase monotonically per actor') - } - changesByActor[change.actor].push(change) - } - - let opsById = {} - for (let op of ops) { - if (op.action === 'del') throw new RangeError('document should not contain del operations') - op.pred = opsById[op.id] ? opsById[op.id].pred : [] - opsById[op.id] = op - for (let succ of op.succ) { - if (!opsById[succ]) { - if (op.elemId) { - const elemId = op.insert ? op.id : op.elemId - opsById[succ] = {id: succ, action: 'del', obj: op.obj, elemId, pred: []} - } else { - opsById[succ] = {id: succ, action: 'del', obj: op.obj, key: op.key, pred: []} - } - } - opsById[succ].pred.push(op.id) - } - delete op.succ - } - for (let op of Object.values(opsById)) { - if (op.action === 'del') ops.push(op) - } - - for (let op of ops) { - const { counter, actorId } = parseOpId(op.id) - const actorChanges = changesByActor[actorId] - // Binary search to find the change that should contain this operation - let left = 0, right = actorChanges.length - while (left < right) { - const index = Math.floor((left + right) / 2) - if (actorChanges[index].maxOp < counter) { - left = index + 1 - } else { - right = index - } - } - if (left >= actorChanges.length) { - throw new RangeError(`Operation ID ${op.id} outside of allowed range`) - } - actorChanges[left].ops.push(op) - } - - for (let change of changes) { - change.ops.sort((op1, op2) => sortOpIds(op1.id, op2.id)) - change.startOp = change.maxOp - change.ops.length + 1 - delete change.maxOp - for (let i = 0; i < change.ops.length; i++) { - const op = change.ops[i], expectedId = `${change.startOp + i}@${change.actor}` - if (op.id !== expectedId) { - throw new RangeError(`Expected opId ${expectedId}, got ${op.id}`) - } - delete op.id - } - } -} - -function decodeDocumentChanges(changes, expectedHeads) { - let heads = {} // change hashes that are not a dependency of any other change - for (let i = 0; i < changes.length; i++) { - let change = changes[i] - change.deps = [] - for (let index of change.depsNum.map(d => d.depsIndex)) { - if (!changes[index] || !changes[index].hash) { - throw new RangeError(`No hash for index ${index} while processing index ${i}`) - } - const hash = changes[index].hash - change.deps.push(hash) - if (heads[hash]) delete heads[hash] - } - change.deps.sort() - delete change.depsNum - - if (change.extraLen_datatype !== VALUE_TYPE.BYTES) { - throw new RangeError(`Bad datatype for extra bytes: ${VALUE_TYPE.BYTES}`) - } - change.extraBytes = change.extraLen - delete change.extraLen_datatype - - // Encoding and decoding again to compute the hash of the change - changes[i] = decodeChange(encodeChange(change)) - heads[changes[i].hash] = true - } - - const actualHeads = Object.keys(heads).sort() - let headsEqual = (actualHeads.length === expectedHeads.length), i = 0 - while (headsEqual && i < actualHeads.length) { - headsEqual = (actualHeads[i] === expectedHeads[i]) - i++ - } - if (!headsEqual) { - throw new RangeError(`Mismatched heads hashes: expected ${expectedHeads.join(', ')}, got ${actualHeads.join(', ')}`) - } -} - -function encodeDocumentHeader(doc) { - const { changesColumns, opsColumns, actorIds, heads, headsIndexes, extraBytes } = doc - for (let column of changesColumns) deflateColumn(column) - for (let column of opsColumns) deflateColumn(column) - - return encodeContainer(CHUNK_TYPE_DOCUMENT, encoder => { - encoder.appendUint53(actorIds.length) - for (let actor of actorIds) { - encoder.appendHexString(actor) - } - encoder.appendUint53(heads.length) - for (let head of heads.sort()) { - encoder.appendRawBytes(hexStringToBytes(head)) - } - encodeColumnInfo(encoder, changesColumns) - encodeColumnInfo(encoder, opsColumns) - for (let column of changesColumns) encoder.appendRawBytes(column.encoder.buffer) - for (let column of opsColumns) encoder.appendRawBytes(column.encoder.buffer) - for (let index of headsIndexes) encoder.appendUint53(index) - if (extraBytes) encoder.appendRawBytes(extraBytes) - }).bytes -} - -function decodeDocumentHeader(buffer) { - const documentDecoder = new Decoder(buffer) - const header = decodeContainerHeader(documentDecoder, true) - const decoder = new Decoder(header.chunkData) - if (!documentDecoder.done) throw new RangeError('Encoded document has trailing data') - if (header.chunkType !== CHUNK_TYPE_DOCUMENT) throw new RangeError(`Unexpected chunk type: ${header.chunkType}`) - - const actorIds = [], numActors = decoder.readUint53() - for (let i = 0; i < numActors; i++) { - actorIds.push(decoder.readHexString()) - } - const heads = [], headsIndexes = [], numHeads = decoder.readUint53() - for (let i = 0; i < numHeads; i++) { - heads.push(bytesToHexString(decoder.readRawBytes(32))) - } - - const changesColumns = decodeColumnInfo(decoder) - const opsColumns = decodeColumnInfo(decoder) - for (let i = 0; i < changesColumns.length; i++) { - changesColumns[i].buffer = decoder.readRawBytes(changesColumns[i].bufferLen) - inflateColumn(changesColumns[i]) - } - for (let i = 0; i < opsColumns.length; i++) { - opsColumns[i].buffer = decoder.readRawBytes(opsColumns[i].bufferLen) - inflateColumn(opsColumns[i]) - } - if (!decoder.done) { - for (let i = 0; i < numHeads; i++) headsIndexes.push(decoder.readUint53()) - } - - const extraBytes = decoder.readRawBytes(decoder.buf.byteLength - decoder.offset) - return { changesColumns, opsColumns, actorIds, heads, headsIndexes, extraBytes } -} - -function decodeDocument(buffer) { - const { changesColumns, opsColumns, actorIds, heads } = decodeDocumentHeader(buffer) - const changes = decodeColumns(changesColumns, actorIds, DOCUMENT_COLUMNS) - const ops = decodeOps(decodeColumns(opsColumns, actorIds, DOC_OPS_COLUMNS), true) - groupChangeOps(changes, ops) - decodeDocumentChanges(changes, heads) - return changes -} - -/** - * DEFLATE-compresses the given column if it is large enough to make the compression worthwhile. - */ -function deflateColumn(column) { - if (column.encoder.buffer.byteLength >= DEFLATE_MIN_SIZE) { - column.encoder = {buffer: pako.deflateRaw(column.encoder.buffer)} - column.columnId |= COLUMN_TYPE_DEFLATE - } -} - -/** - * Decompresses the given column if it is DEFLATE-compressed. - */ -function inflateColumn(column) { - if ((column.columnId & COLUMN_TYPE_DEFLATE) !== 0) { - column.buffer = pako.inflateRaw(column.buffer) - column.columnId ^= COLUMN_TYPE_DEFLATE - } -} - -module.exports = { - COLUMN_TYPE, VALUE_TYPE, ACTIONS, OBJECT_TYPE, DOC_OPS_COLUMNS, CHANGE_COLUMNS, DOCUMENT_COLUMNS, - encoderByColumnId, decoderByColumnId, makeDecoders, decodeValue, - splitContainers, encodeChange, decodeChangeColumns, decodeChange, decodeChangeMeta, decodeChanges, - encodeDocumentHeader, decodeDocumentHeader, decodeDocument -} - -},{"../src/common":85,"./encoding":48,"fast-sha256":203,"pako":63}],48:[function(require,module,exports){ -/** - * UTF-8 decoding and encoding using API that is supported in Node >= 12 and modern browsers: - * https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encode - * https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode - * If you're running in an environment where it's not available, please use a polyfill, such as: - * https://github.com/anonyco/FastestSmallestTextEncoderDecoder - */ -const utf8encoder = new TextEncoder() -const utf8decoder = new TextDecoder('utf-8') - -function stringToUtf8(string) { - return utf8encoder.encode(string) -} - -function utf8ToString(buffer) { - return utf8decoder.decode(buffer) -} - -/** - * Converts a string consisting of hexadecimal digits into an Uint8Array. - */ -function hexStringToBytes(value) { - if (typeof value !== 'string') { - throw new TypeError('value is not a string') - } - if (!/^([0-9a-f][0-9a-f])*$/.test(value)) { - throw new RangeError('value is not hexadecimal') - } - if (value === '') { - return new Uint8Array(0) - } else { - return new Uint8Array(value.match(/../g).map(b => parseInt(b, 16))) - } -} - -const NIBBLE_TO_HEX = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] -const BYTE_TO_HEX = new Array(256) -for (let i = 0; i < 256; i++) { - BYTE_TO_HEX[i] = `${NIBBLE_TO_HEX[(i >>> 4) & 0xf]}${NIBBLE_TO_HEX[i & 0xf]}`; -} - -/** - * Converts a Uint8Array into the equivalent hexadecimal string. - */ -function bytesToHexString(bytes) { - let hex = '', len = bytes.byteLength - for (let i = 0; i < len; i++) { - hex += BYTE_TO_HEX[bytes[i]] - } - return hex -} - -/** - * Wrapper around an Uint8Array that allows values to be appended to the buffer, - * and that automatically grows the buffer when space runs out. - */ -class Encoder { - constructor() { - this.buf = new Uint8Array(16) - this.offset = 0 - } - - /** - * Returns the byte array containing the encoded data. - */ - get buffer() { - this.finish() - return this.buf.subarray(0, this.offset) - } - - /** - * Reallocates the encoder's buffer to be bigger. - */ - grow(minSize = 0) { - let newSize = this.buf.byteLength * 4 - while (newSize < minSize) newSize *= 2 - const newBuf = new Uint8Array(newSize) - newBuf.set(this.buf, 0) - this.buf = newBuf - return this - } - - /** - * Appends one byte (0 to 255) to the buffer. - */ - appendByte(value) { - if (this.offset >= this.buf.byteLength) this.grow() - this.buf[this.offset] = value - this.offset += 1 - } - - /** - * Encodes a 32-bit nonnegative integer in a variable number of bytes using - * the LEB128 encoding scheme (https://en.wikipedia.org/wiki/LEB128) and - * appends it to the buffer. Returns the number of bytes written. - */ - appendUint32(value) { - if (!Number.isInteger(value)) throw new RangeError('value is not an integer') - if (value < 0 || value > 0xffffffff) throw new RangeError('number out of range') - - const numBytes = Math.max(1, Math.ceil((32 - Math.clz32(value)) / 7)) - if (this.offset + numBytes > this.buf.byteLength) this.grow() - - for (let i = 0; i < numBytes; i++) { - this.buf[this.offset + i] = (value & 0x7f) | (i === numBytes - 1 ? 0x00 : 0x80) - value >>>= 7 // zero-filling right shift - } - this.offset += numBytes - return numBytes - } - - /** - * Encodes a 32-bit signed integer in a variable number of bytes using the - * LEB128 encoding scheme (https://en.wikipedia.org/wiki/LEB128) and appends - * it to the buffer. Returns the number of bytes written. - */ - appendInt32(value) { - if (!Number.isInteger(value)) throw new RangeError('value is not an integer') - if (value < -0x80000000 || value > 0x7fffffff) throw new RangeError('number out of range') - - const numBytes = Math.ceil((33 - Math.clz32(value >= 0 ? value : -value - 1)) / 7) - if (this.offset + numBytes > this.buf.byteLength) this.grow() - - for (let i = 0; i < numBytes; i++) { - this.buf[this.offset + i] = (value & 0x7f) | (i === numBytes - 1 ? 0x00 : 0x80) - value >>= 7 // sign-propagating right shift - } - this.offset += numBytes - return numBytes - } - - /** - * Encodes a nonnegative integer in a variable number of bytes using the LEB128 - * encoding scheme, up to the maximum size of integers supported by JavaScript - * (53 bits). - */ - appendUint53(value) { - if (!Number.isInteger(value)) throw new RangeError('value is not an integer') - if (value < 0 || value > Number.MAX_SAFE_INTEGER) { - throw new RangeError('number out of range') - } - const high32 = Math.floor(value / 0x100000000) - const low32 = (value & 0xffffffff) >>> 0 // right shift to interpret as unsigned - return this.appendUint64(high32, low32) - } - - /** - * Encodes a signed integer in a variable number of bytes using the LEB128 - * encoding scheme, up to the maximum size of integers supported by JavaScript - * (53 bits). - */ - appendInt53(value) { - if (!Number.isInteger(value)) throw new RangeError('value is not an integer') - if (value < Number.MIN_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER) { - throw new RangeError('number out of range') - } - const high32 = Math.floor(value / 0x100000000) - const low32 = (value & 0xffffffff) >>> 0 // right shift to interpret as unsigned - return this.appendInt64(high32, low32) - } - - /** - * Encodes a 64-bit nonnegative integer in a variable number of bytes using - * the LEB128 encoding scheme, and appends it to the buffer. The number is - * given as two 32-bit halves since JavaScript cannot accurately represent - * integers with more than 53 bits in a single variable. - */ - appendUint64(high32, low32) { - if (!Number.isInteger(high32) || !Number.isInteger(low32)) { - throw new RangeError('value is not an integer') - } - if (high32 < 0 || high32 > 0xffffffff || low32 < 0 || low32 > 0xffffffff) { - throw new RangeError('number out of range') - } - if (high32 === 0) return this.appendUint32(low32) - - const numBytes = Math.ceil((64 - Math.clz32(high32)) / 7) - if (this.offset + numBytes > this.buf.byteLength) this.grow() - for (let i = 0; i < 4; i++) { - this.buf[this.offset + i] = (low32 & 0x7f) | 0x80 - low32 >>>= 7 // zero-filling right shift - } - this.buf[this.offset + 4] = (low32 & 0x0f) | ((high32 & 0x07) << 4) | (numBytes === 5 ? 0x00 : 0x80) - high32 >>>= 3 - for (let i = 5; i < numBytes; i++) { - this.buf[this.offset + i] = (high32 & 0x7f) | (i === numBytes - 1 ? 0x00 : 0x80) - high32 >>>= 7 - } - this.offset += numBytes - return numBytes - } - - /** - * Encodes a 64-bit signed integer in a variable number of bytes using the - * LEB128 encoding scheme, and appends it to the buffer. The number is given - * as two 32-bit halves since JavaScript cannot accurately represent integers - * with more than 53 bits in a single variable. The sign of the 64-bit - * number is determined by the sign of the `high32` half; the sign of the - * `low32` half is ignored. - */ - appendInt64(high32, low32) { - if (!Number.isInteger(high32) || !Number.isInteger(low32)) { - throw new RangeError('value is not an integer') - } - if (high32 < -0x80000000 || high32 > 0x7fffffff || low32 < -0x80000000 || low32 > 0xffffffff) { - throw new RangeError('number out of range') - } - low32 >>>= 0 // interpret as unsigned - if (high32 === 0 && low32 <= 0x7fffffff) return this.appendInt32(low32) - if (high32 === -1 && low32 >= 0x80000000) return this.appendInt32(low32 - 0x100000000) - - const numBytes = Math.ceil((65 - Math.clz32(high32 >= 0 ? high32 : -high32 - 1)) / 7) - if (this.offset + numBytes > this.buf.byteLength) this.grow() - for (let i = 0; i < 4; i++) { - this.buf[this.offset + i] = (low32 & 0x7f) | 0x80 - low32 >>>= 7 // zero-filling right shift - } - this.buf[this.offset + 4] = (low32 & 0x0f) | ((high32 & 0x07) << 4) | (numBytes === 5 ? 0x00 : 0x80) - high32 >>= 3 // sign-propagating right shift - for (let i = 5; i < numBytes; i++) { - this.buf[this.offset + i] = (high32 & 0x7f) | (i === numBytes - 1 ? 0x00 : 0x80) - high32 >>= 7 - } - this.offset += numBytes - return numBytes - } - - /** - * Appends the contents of byte buffer `data` to the buffer. Returns the - * number of bytes appended. - */ - appendRawBytes(data) { - if (this.offset + data.byteLength > this.buf.byteLength) { - this.grow(this.offset + data.byteLength) - } - this.buf.set(data, this.offset) - this.offset += data.byteLength - return data.byteLength - } - - /** - * Appends a UTF-8 string to the buffer, without any metadata. Returns the - * number of bytes appended. - */ - appendRawString(value) { - if (typeof value !== 'string') throw new TypeError('value is not a string') - return this.appendRawBytes(stringToUtf8(value)) - } - - /** - * Appends the contents of byte buffer `data` to the buffer, prefixed with the - * number of bytes in the buffer (as a LEB128-encoded unsigned integer). - */ - appendPrefixedBytes(data) { - this.appendUint53(data.byteLength) - this.appendRawBytes(data) - return this - } - - /** - * Appends a UTF-8 string to the buffer, prefixed with its length in bytes - * (where the length is encoded as an unsigned LEB128 integer). - */ - appendPrefixedString(value) { - if (typeof value !== 'string') throw new TypeError('value is not a string') - this.appendPrefixedBytes(stringToUtf8(value)) - return this - } - - /** - * Takes a value, which must be a string consisting only of hexadecimal - * digits, maps it to a byte array, and appends it to the buffer, prefixed - * with its length in bytes. - */ - appendHexString(value) { - this.appendPrefixedBytes(hexStringToBytes(value)) - return this - } - - /** - * Flushes any unwritten data to the buffer. Call this before reading from - * the buffer constructed by this Encoder. - */ - finish() { - } -} - -/** - * Counterpart to Encoder. Wraps a Uint8Array buffer with a cursor indicating - * the current decoding position, and allows values to be incrementally read by - * decoding the bytes at the current position. - */ -class Decoder { - constructor(buffer) { - if (!(buffer instanceof Uint8Array)) { - throw new TypeError(`Not a byte array: ${buffer}`) - } - this.buf = buffer - this.offset = 0 - } - - /** - * Returns false if there is still data to be read at the current decoding - * position, and true if we are at the end of the buffer. - */ - get done() { - return this.offset === this.buf.byteLength - } - - /** - * Resets the cursor position, so that the next read goes back to the - * beginning of the buffer. - */ - reset() { - this.offset = 0 - } - - /** - * Moves the current decoding position forward by the specified number of - * bytes, without decoding anything. - */ - skip(bytes) { - if (this.offset + bytes > this.buf.byteLength) { - throw new RangeError('cannot skip beyond end of buffer') - } - this.offset += bytes - } - - /** - * Reads one byte (0 to 255) from the buffer. - */ - readByte() { - this.offset += 1 - return this.buf[this.offset - 1] - } - - /** - * Reads a LEB128-encoded unsigned integer from the current position in the buffer. - * Throws an exception if the value doesn't fit in a 32-bit unsigned int. - */ - readUint32() { - let result = 0, shift = 0 - while (this.offset < this.buf.byteLength) { - const nextByte = this.buf[this.offset] - if (shift === 28 && (nextByte & 0xf0) !== 0) { // more than 5 bytes, or value > 0xffffffff - throw new RangeError('number out of range') - } - result = (result | (nextByte & 0x7f) << shift) >>> 0 // right shift to interpret value as unsigned - shift += 7 - this.offset++ - if ((nextByte & 0x80) === 0) return result - } - throw new RangeError('buffer ended with incomplete number') - } - - /** - * Reads a LEB128-encoded signed integer from the current position in the buffer. - * Throws an exception if the value doesn't fit in a 32-bit signed int. - */ - readInt32() { - let result = 0, shift = 0 - while (this.offset < this.buf.byteLength) { - const nextByte = this.buf[this.offset] - if ((shift === 28 && (nextByte & 0x80) !== 0) || // more than 5 bytes - (shift === 28 && (nextByte & 0x40) === 0 && (nextByte & 0x38) !== 0) || // positive int > 0x7fffffff - (shift === 28 && (nextByte & 0x40) !== 0 && (nextByte & 0x38) !== 0x38)) { // negative int < -0x80000000 - throw new RangeError('number out of range') - } - result |= (nextByte & 0x7f) << shift - shift += 7 - this.offset++ - - if ((nextByte & 0x80) === 0) { - if ((nextByte & 0x40) === 0 || shift > 28) { - return result // positive, or negative value that doesn't need sign-extending - } else { - return result | (-1 << shift) // sign-extend negative integer - } - } - } - throw new RangeError('buffer ended with incomplete number') - } - - /** - * Reads a LEB128-encoded unsigned integer from the current position in the - * buffer. Allows any integer that can be safely represented by JavaScript - * (up to 2^53 - 1), and throws an exception outside of that range. - */ - readUint53() { - const { low32, high32 } = this.readUint64() - if (high32 < 0 || high32 > 0x1fffff) { - throw new RangeError('number out of range') - } - return high32 * 0x100000000 + low32 - } - - /** - * Reads a LEB128-encoded signed integer from the current position in the - * buffer. Allows any integer that can be safely represented by JavaScript - * (between -(2^53 - 1) and 2^53 - 1), throws an exception outside of that range. - */ - readInt53() { - const { low32, high32 } = this.readInt64() - if (high32 < -0x200000 || (high32 === -0x200000 && low32 === 0) || high32 > 0x1fffff) { - throw new RangeError('number out of range') - } - return high32 * 0x100000000 + low32 - } - - /** - * Reads a LEB128-encoded unsigned integer from the current position in the - * buffer. Throws an exception if the value doesn't fit in a 64-bit unsigned - * int. Returns the number in two 32-bit halves, as an object of the form - * `{high32, low32}`. - */ - readUint64() { - let low32 = 0, high32 = 0, shift = 0 - while (this.offset < this.buf.byteLength && shift <= 28) { - const nextByte = this.buf[this.offset] - low32 = (low32 | (nextByte & 0x7f) << shift) >>> 0 // right shift to interpret value as unsigned - if (shift === 28) { - high32 = (nextByte & 0x70) >>> 4 - } - shift += 7 - this.offset++ - if ((nextByte & 0x80) === 0) return { high32, low32 } - } - - shift = 3 - while (this.offset < this.buf.byteLength) { - const nextByte = this.buf[this.offset] - if (shift === 31 && (nextByte & 0xfe) !== 0) { // more than 10 bytes, or value > 2^64 - 1 - throw new RangeError('number out of range') - } - high32 = (high32 | (nextByte & 0x7f) << shift) >>> 0 - shift += 7 - this.offset++ - if ((nextByte & 0x80) === 0) return { high32, low32 } - } - throw new RangeError('buffer ended with incomplete number') - } - - /** - * Reads a LEB128-encoded signed integer from the current position in the - * buffer. Throws an exception if the value doesn't fit in a 64-bit signed - * int. Returns the number in two 32-bit halves, as an object of the form - * `{high32, low32}`. The `low32` half is always non-negative, and the - * sign of the `high32` half indicates the sign of the 64-bit number. - */ - readInt64() { - let low32 = 0, high32 = 0, shift = 0 - while (this.offset < this.buf.byteLength && shift <= 28) { - const nextByte = this.buf[this.offset] - low32 = (low32 | (nextByte & 0x7f) << shift) >>> 0 // right shift to interpret value as unsigned - if (shift === 28) { - high32 = (nextByte & 0x70) >>> 4 - } - shift += 7 - this.offset++ - if ((nextByte & 0x80) === 0) { - if ((nextByte & 0x40) !== 0) { // sign-extend negative integer - if (shift < 32) low32 = (low32 | (-1 << shift)) >>> 0 - high32 |= -1 << Math.max(shift - 32, 0) - } - return { high32, low32 } - } - } - - shift = 3 - while (this.offset < this.buf.byteLength) { - const nextByte = this.buf[this.offset] - // On the 10th byte there are only two valid values: all 7 value bits zero - // (if the value is positive) or all 7 bits one (if the value is negative) - if (shift === 31 && nextByte !== 0 && nextByte !== 0x7f) { - throw new RangeError('number out of range') - } - high32 |= (nextByte & 0x7f) << shift - shift += 7 - this.offset++ - if ((nextByte & 0x80) === 0) { - if ((nextByte & 0x40) !== 0 && shift < 32) { // sign-extend negative integer - high32 |= -1 << shift - } - return { high32, low32 } - } - } - throw new RangeError('buffer ended with incomplete number') - } - - /** - * Extracts a subarray `length` bytes in size, starting from the current - * position in the buffer, and moves the position forward. - */ - readRawBytes(length) { - const start = this.offset - if (start + length > this.buf.byteLength) { - throw new RangeError('subarray exceeds buffer size') - } - this.offset += length - return this.buf.subarray(start, this.offset) - } - - /** - * Extracts `length` bytes from the buffer, starting from the current position, - * and returns the UTF-8 string decoding of those bytes. - */ - readRawString(length) { - return utf8ToString(this.readRawBytes(length)) - } - - /** - * Extracts a subarray from the current position in the buffer, prefixed with - * its length in bytes (encoded as an unsigned LEB128 integer). - */ - readPrefixedBytes() { - return this.readRawBytes(this.readUint53()) - } - - /** - * Reads a UTF-8 string from the current position in the buffer, prefixed with its - * length in bytes (where the length is encoded as an unsigned LEB128 integer). - */ - readPrefixedString() { - return utf8ToString(this.readPrefixedBytes()) - } - - /** - * Reads a byte array from the current position in the buffer, prefixed with its - * length in bytes. Returns that byte array converted to a hexadecimal string. - */ - readHexString() { - return bytesToHexString(this.readPrefixedBytes()) - } -} - -/** - * An encoder that uses run-length encoding to compress sequences of repeated - * values. The constructor argument specifies the type of values, which may be - * either 'int', 'uint', or 'utf8'. Besides valid values of the selected - * datatype, values may also be null. - * - * The encoded buffer starts with a LEB128-encoded signed integer, the - * repetition count. The interpretation of the following values depends on this - * repetition count: - * - If this number is a positive value n, the next value in the buffer - * (encoded as the specified datatype) is repeated n times in the sequence. - * - If the repetition count is a negative value -n, then the next n values - * (encoded as the specified datatype) in the buffer are treated as a - * literal, i.e. they appear in the sequence without any further - * interpretation or repetition. - * - If the repetition count is zero, then the next value in the buffer is a - * LEB128-encoded unsigned integer indicating the number of null values - * that appear at the current position in the sequence. - * - * After one of these three has completed, the process repeats, starting again - * with a repetition count, until we reach the end of the buffer. - */ -class RLEEncoder extends Encoder { - constructor(type) { - super() - this.type = type - this.state = 'empty' - this.lastValue = undefined - this.count = 0 - this.literal = [] - } - - /** - * Appends a new value to the sequence. If `repetitions` is given, the value is repeated - * `repetitions` times. - */ - appendValue(value, repetitions = 1) { - this._appendValue(value, repetitions) - } - - /** - * Like `appendValue()`, but this method is not overridden by `DeltaEncoder`. - */ - _appendValue(value, repetitions = 1) { - if (repetitions <= 0) return - if (this.state === 'empty') { - this.state = (value === null ? 'nulls' : (repetitions === 1 ? 'loneValue' : 'repetition')) - this.lastValue = value - this.count = repetitions - } else if (this.state === 'loneValue') { - if (value === null) { - this.flush() - this.state = 'nulls' - this.count = repetitions - } else if (value === this.lastValue) { - this.state = 'repetition' - this.count = 1 + repetitions - } else if (repetitions > 1) { - this.flush() - this.state = 'repetition' - this.count = repetitions - this.lastValue = value - } else { - this.state = 'literal' - this.literal = [this.lastValue] - this.lastValue = value - } - } else if (this.state === 'repetition') { - if (value === null) { - this.flush() - this.state = 'nulls' - this.count = repetitions - } else if (value === this.lastValue) { - this.count += repetitions - } else if (repetitions > 1) { - this.flush() - this.state = 'repetition' - this.count = repetitions - this.lastValue = value - } else { - this.flush() - this.state = 'loneValue' - this.lastValue = value - } - } else if (this.state === 'literal') { - if (value === null) { - this.literal.push(this.lastValue) - this.flush() - this.state = 'nulls' - this.count = repetitions - } else if (value === this.lastValue) { - this.flush() - this.state = 'repetition' - this.count = 1 + repetitions - } else if (repetitions > 1) { - this.literal.push(this.lastValue) - this.flush() - this.state = 'repetition' - this.count = repetitions - this.lastValue = value - } else { - this.literal.push(this.lastValue) - this.lastValue = value - } - } else if (this.state === 'nulls') { - if (value === null) { - this.count += repetitions - } else if (repetitions > 1) { - this.flush() - this.state = 'repetition' - this.count = repetitions - this.lastValue = value - } else { - this.flush() - this.state = 'loneValue' - this.lastValue = value - } - } - } - - /** - * Copies values from the RLEDecoder `decoder` into this encoder. The `options` object may - * contain the following keys: - * - `count`: The number of values to copy. If not specified, copies all remaining values. - * - `sumValues`: If true, the function computes the sum of all numeric values as they are - * copied (null values are counted as zero), and returns that number. - * - `sumShift`: If set, values are shifted right by `sumShift` bits before adding to the sum. - * - * Returns an object of the form `{nonNullValues, sum}` where `nonNullValues` is the number of - * non-null values copied, and `sum` is the sum (only if the `sumValues` option is set). - */ - copyFrom(decoder, options = {}) { - const { count, sumValues, sumShift } = options - if (!(decoder instanceof RLEDecoder) || (decoder.type !== this.type)) { - throw new TypeError('incompatible type of decoder') - } - let remaining = (typeof count === 'number' ? count : Number.MAX_SAFE_INTEGER) - let nonNullValues = 0, sum = 0 - if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) - if (remaining === 0 || decoder.done) return sumValues ? {nonNullValues, sum} : {nonNullValues} - - // Copy a value so that we have a well-defined starting state. NB: when super.copyFrom() is - // called by the DeltaEncoder subclass, the following calls to readValue() and appendValue() - // refer to the overridden methods, while later readRecord(), readRawValue() and _appendValue() - // calls refer to the non-overridden RLEDecoder/RLEEncoder methods. - let firstValue = decoder.readValue() - if (firstValue === null) { - const numNulls = Math.min(decoder.count + 1, remaining) - remaining -= numNulls - decoder.count -= numNulls - 1 - this.appendValue(null, numNulls) - if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) - if (remaining === 0 || decoder.done) return sumValues ? {nonNullValues, sum} : {nonNullValues} - firstValue = decoder.readValue() - if (firstValue === null) throw new RangeError('null run must be followed by non-null value') - } - this.appendValue(firstValue) - remaining-- - nonNullValues++ - if (sumValues) sum += (sumShift ? (firstValue >>> sumShift) : firstValue) - if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) - if (remaining === 0 || decoder.done) return sumValues ? {nonNullValues, sum} : {nonNullValues} - - // Copy data at the record level without expanding repetitions - let firstRun = (decoder.count > 0) - while (remaining > 0 && !decoder.done) { - if (!firstRun) decoder.readRecord() - const numValues = Math.min(decoder.count, remaining) - decoder.count -= numValues - - if (decoder.state === 'literal') { - nonNullValues += numValues - for (let i = 0; i < numValues; i++) { - if (decoder.done) throw new RangeError('incomplete literal') - const value = decoder.readRawValue() - if (value === decoder.lastValue) throw new RangeError('Repetition of values is not allowed in literal') - decoder.lastValue = value - this._appendValue(value) - if (sumValues) sum += (sumShift ? (value >>> sumShift) : value) - } - } else if (decoder.state === 'repetition') { - nonNullValues += numValues - if (sumValues) sum += numValues * (sumShift ? (decoder.lastValue >>> sumShift) : decoder.lastValue) - const value = decoder.lastValue - this._appendValue(value) - if (numValues > 1) { - this._appendValue(value) - if (this.state !== 'repetition') throw new RangeError(`Unexpected state ${this.state}`) - this.count += numValues - 2 - } - } else if (decoder.state === 'nulls') { - this._appendValue(null) - if (this.state !== 'nulls') throw new RangeError(`Unexpected state ${this.state}`) - this.count += numValues - 1 - } - - firstRun = false - remaining -= numValues - } - if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) - return sumValues ? {nonNullValues, sum} : {nonNullValues} - } - - /** - * Private method, do not call from outside the class. - */ - flush() { - if (this.state === 'loneValue') { - this.appendInt32(-1) - this.appendRawValue(this.lastValue) - } else if (this.state === 'repetition') { - this.appendInt53(this.count) - this.appendRawValue(this.lastValue) - } else if (this.state === 'literal') { - this.appendInt53(-this.literal.length) - for (let v of this.literal) this.appendRawValue(v) - } else if (this.state === 'nulls') { - this.appendInt32(0) - this.appendUint53(this.count) - } - this.state = 'empty' - } - - /** - * Private method, do not call from outside the class. - */ - appendRawValue(value) { - if (this.type === 'int') { - this.appendInt53(value) - } else if (this.type === 'uint') { - this.appendUint53(value) - } else if (this.type === 'utf8') { - this.appendPrefixedString(value) - } else { - throw new RangeError(`Unknown RLEEncoder datatype: ${this.type}`) - } - } - - /** - * Flushes any unwritten data to the buffer. Call this before reading from - * the buffer constructed by this Encoder. - */ - finish() { - if (this.state === 'literal') this.literal.push(this.lastValue) - // Don't write anything if the only values we have seen are nulls - if (this.state !== 'nulls' || this.offset > 0) this.flush() - } -} - -/** - * Counterpart to RLEEncoder: reads values from an RLE-compressed sequence, - * returning nulls and repeated values as required. - */ -class RLEDecoder extends Decoder { - constructor(type, buffer) { - super(buffer) - this.type = type - this.lastValue = undefined - this.count = 0 - this.state = undefined - } - - /** - * Returns false if there is still data to be read at the current decoding - * position, and true if we are at the end of the buffer. - */ - get done() { - return (this.count === 0) && (this.offset === this.buf.byteLength) - } - - /** - * Resets the cursor position, so that the next read goes back to the - * beginning of the buffer. - */ - reset() { - this.offset = 0 - this.lastValue = undefined - this.count = 0 - this.state = undefined - } - - /** - * Returns the next value (or null) in the sequence. - */ - readValue() { - if (this.done) return null - if (this.count === 0) this.readRecord() - this.count -= 1 - if (this.state === 'literal') { - const value = this.readRawValue() - if (value === this.lastValue) throw new RangeError('Repetition of values is not allowed in literal') - this.lastValue = value - return value - } else { - return this.lastValue - } - } - - /** - * Discards the next `numSkip` values in the sequence. - */ - skipValues(numSkip) { - while (numSkip > 0 && !this.done) { - if (this.count === 0) { - this.count = this.readInt53() - if (this.count > 0) { - this.lastValue = (this.count <= numSkip) ? this.skipRawValues(1) : this.readRawValue() - this.state = 'repetition' - } else if (this.count < 0) { - this.count = -this.count - this.state = 'literal' - } else { // this.count == 0 - this.count = this.readUint53() - this.lastValue = null - this.state = 'nulls' - } - } - - const consume = Math.min(numSkip, this.count) - if (this.state === 'literal') this.skipRawValues(consume) - numSkip -= consume - this.count -= consume - } - } - - /** - * Private method, do not call from outside the class. - * Reads a repetition count from the buffer and sets up the state appropriately. - */ - readRecord() { - this.count = this.readInt53() - if (this.count > 1) { - const value = this.readRawValue() - if ((this.state === 'repetition' || this.state === 'literal') && this.lastValue === value) { - throw new RangeError('Successive repetitions with the same value are not allowed') - } - this.state = 'repetition' - this.lastValue = value - } else if (this.count === 1) { - throw new RangeError('Repetition count of 1 is not allowed, use a literal instead') - } else if (this.count < 0) { - this.count = -this.count - if (this.state === 'literal') throw new RangeError('Successive literals are not allowed') - this.state = 'literal' - } else { // this.count == 0 - if (this.state === 'nulls') throw new RangeError('Successive null runs are not allowed') - this.count = this.readUint53() - if (this.count === 0) throw new RangeError('Zero-length null runs are not allowed') - this.lastValue = null - this.state = 'nulls' - } - } - - /** - * Private method, do not call from outside the class. - * Reads one value of the datatype configured on construction. - */ - readRawValue() { - if (this.type === 'int') { - return this.readInt53() - } else if (this.type === 'uint') { - return this.readUint53() - } else if (this.type === 'utf8') { - return this.readPrefixedString() - } else { - throw new RangeError(`Unknown RLEDecoder datatype: ${this.type}`) - } - } - - /** - * Private method, do not call from outside the class. - * Skips over `num` values of the datatype configured on construction. - */ - skipRawValues(num) { - if (this.type === 'utf8') { - for (let i = 0; i < num; i++) this.skip(this.readUint53()) - } else { - while (num > 0 && this.offset < this.buf.byteLength) { - if ((this.buf[this.offset] & 0x80) === 0) num-- - this.offset++ - } - if (num > 0) throw new RangeError('cannot skip beyond end of buffer') - } - } -} - -/** - * A variant of RLEEncoder: rather than storing the actual values passed to - * appendValue(), this version stores only the first value, and for all - * subsequent values it stores the difference to the previous value. This - * encoding is good when values tend to come in sequentially incrementing runs, - * because the delta between successive values is 1, and repeated values of 1 - * are easily compressed with run-length encoding. - * - * Null values are also allowed, as with RLEEncoder. - */ -class DeltaEncoder extends RLEEncoder { - constructor() { - super('int') - this.absoluteValue = 0 - } - - /** - * Appends a new integer value to the sequence. If `repetitions` is given, the value is repeated - * `repetitions` times. - */ - appendValue(value, repetitions = 1) { - if (repetitions <= 0) return - if (typeof value === 'number') { - super.appendValue(value - this.absoluteValue, 1) - this.absoluteValue = value - if (repetitions > 1) super.appendValue(0, repetitions - 1) - } else { - super.appendValue(value, repetitions) - } - } - - /** - * Copies values from the DeltaDecoder `decoder` into this encoder. The `options` object may - * contain the key `count`, indicating the number of values to copy. If not specified, copies - * all remaining values in the decoder. - */ - copyFrom(decoder, options = {}) { - if (options.sumValues) { - throw new RangeError('unsupported options for DeltaEncoder.copyFrom()') - } - if (!(decoder instanceof DeltaDecoder)) { - throw new TypeError('incompatible type of decoder') - } - - let remaining = options.count - if (remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${remaining} values`) - if (remaining === 0 || decoder.done) return - - // Copy any null values, and the first non-null value, so that appendValue() computes the - // difference between the encoder's last value and the decoder's first (absolute) value. - let value = decoder.readValue(), nulls = 0 - this.appendValue(value) - if (value === null) { - nulls = decoder.count + 1 - if (remaining !== undefined && remaining < nulls) nulls = remaining - decoder.count -= nulls - 1 - this.count += nulls - 1 - if (remaining > nulls && decoder.done) throw new RangeError(`cannot copy ${remaining} values`) - if (remaining === nulls || decoder.done) return - - // The next value read is certain to be non-null because we're not at the end of the decoder, - // and a run of nulls must be followed by a run of non-nulls. - if (decoder.count === 0) this.appendValue(decoder.readValue()) - } - - // Once we have the first value, the subsequent relative values can be copied verbatim without - // any further processing. Note that the first value copied by super.copyFrom() is an absolute - // value, while subsequent values are relative. Thus, the sum of all of the (non-null) copied - // values must equal the absolute value of the final element copied. - if (remaining !== undefined) remaining -= nulls + 1 - const { nonNullValues, sum } = super.copyFrom(decoder, {count: remaining, sumValues: true}) - if (nonNullValues > 0) { - this.absoluteValue = sum - decoder.absoluteValue = sum - } - } -} - -/** - * Counterpart to DeltaEncoder: reads values from a delta-compressed sequence of - * numbers (may include null values). - */ -class DeltaDecoder extends RLEDecoder { - constructor(buffer) { - super('int', buffer) - this.absoluteValue = 0 - } - - /** - * Resets the cursor position, so that the next read goes back to the - * beginning of the buffer. - */ - reset() { - this.offset = 0 - this.lastValue = undefined - this.count = 0 - this.state = undefined - this.absoluteValue = 0 - } - - /** - * Returns the next integer (or null) value in the sequence. - */ - readValue() { - const value = super.readValue() - if (value === null) return null - this.absoluteValue += value - return this.absoluteValue - } - - /** - * Discards the next `numSkip` values in the sequence. - */ - skipValues(numSkip) { - while (numSkip > 0 && !this.done) { - if (this.count === 0) this.readRecord() - const consume = Math.min(numSkip, this.count) - if (this.state === 'literal') { - for (let i = 0; i < consume; i++) { - this.lastValue = this.readRawValue() - this.absoluteValue += this.lastValue - } - } else if (this.state === 'repetition') { - this.absoluteValue += consume * this.lastValue - } - numSkip -= consume - this.count -= consume - } - } -} - -/** - * Encodes a sequence of boolean values by mapping it to a sequence of integers: - * the number of false values, followed by the number of true values, followed - * by the number of false values, and so on. Each number is encoded as a LEB128 - * unsigned integer. This encoding is a bit like RLEEncoder, except that we - * only encode the repetition count but not the actual value, since the values - * just alternate between false and true (starting with false). - */ -class BooleanEncoder extends Encoder { - constructor() { - super() - this.lastValue = false - this.count = 0 - } - - /** - * Appends a new value to the sequence. If `repetitions` is given, the value is repeated - * `repetitions` times. - */ - appendValue(value, repetitions = 1) { - if (value !== false && value !== true) { - throw new RangeError(`Unsupported value for BooleanEncoder: ${value}`) - } - if (repetitions <= 0) return - if (this.lastValue === value) { - this.count += repetitions - } else { - this.appendUint53(this.count) - this.lastValue = value - this.count = repetitions - } - } - - /** - * Copies values from the BooleanDecoder `decoder` into this encoder. The `options` object may - * contain the key `count`, indicating the number of values to copy. If not specified, copies - * all remaining values in the decoder. - */ - copyFrom(decoder, options = {}) { - if (!(decoder instanceof BooleanDecoder)) { - throw new TypeError('incompatible type of decoder') - } - - const { count } = options - let remaining = (typeof count === 'number' ? count : Number.MAX_SAFE_INTEGER) - if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) - if (remaining === 0 || decoder.done) return - - // Copy one value to bring decoder and encoder state into sync, then finish that value's repetitions - this.appendValue(decoder.readValue()) - remaining-- - const firstCopy = Math.min(decoder.count, remaining) - this.count += firstCopy - decoder.count -= firstCopy - remaining -= firstCopy - - while (remaining > 0 && !decoder.done) { - decoder.count = decoder.readUint53() - if (decoder.count === 0) throw new RangeError('Zero-length runs are not allowed') - decoder.lastValue = !decoder.lastValue - this.appendUint53(this.count) - - const numCopied = Math.min(decoder.count, remaining) - this.count = numCopied - this.lastValue = decoder.lastValue - decoder.count -= numCopied - remaining -= numCopied - } - - if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) - } - - /** - * Flushes any unwritten data to the buffer. Call this before reading from - * the buffer constructed by this Encoder. - */ - finish() { - if (this.count > 0) { - this.appendUint53(this.count) - this.count = 0 - } - } -} - -/** - * Counterpart to BooleanEncoder: reads boolean values from a runlength-encoded - * sequence. - */ -class BooleanDecoder extends Decoder { - constructor(buffer) { - super(buffer) - this.lastValue = true // is negated the first time we read a count - this.firstRun = true - this.count = 0 - } - - /** - * Returns false if there is still data to be read at the current decoding - * position, and true if we are at the end of the buffer. - */ - get done() { - return (this.count === 0) && (this.offset === this.buf.byteLength) - } - - /** - * Resets the cursor position, so that the next read goes back to the - * beginning of the buffer. - */ - reset() { - this.offset = 0 - this.lastValue = true - this.firstRun = true - this.count = 0 - } - - /** - * Returns the next value in the sequence. - */ - readValue() { - if (this.done) return false - while (this.count === 0) { - this.count = this.readUint53() - this.lastValue = !this.lastValue - if (this.count === 0 && !this.firstRun) { - throw new RangeError('Zero-length runs are not allowed') - } - this.firstRun = false - } - this.count -= 1 - return this.lastValue - } - - /** - * Discards the next `numSkip` values in the sequence. - */ - skipValues(numSkip) { - while (numSkip > 0 && !this.done) { - if (this.count === 0) { - this.count = this.readUint53() - this.lastValue = !this.lastValue - if (this.count === 0) throw new RangeError('Zero-length runs are not allowed') - } - if (this.count < numSkip) { - numSkip -= this.count - this.count = 0 - } else { - this.count -= numSkip - numSkip = 0 - } - } - } -} - -module.exports = { - stringToUtf8, utf8ToString, hexStringToBytes, bytesToHexString, - Encoder, Decoder, RLEEncoder, RLEDecoder, DeltaEncoder, DeltaDecoder, BooleanEncoder, BooleanDecoder -} - -},{}],49:[function(require,module,exports){ -const { init, clone, free, applyChanges, applyLocalChange, save, load, loadChanges, getPatch, getHeads, getAllChanges, getChanges, getChangesAdded, getChangeByHash, getMissingDeps } = require("./backend") -const { receiveSyncMessage, generateSyncMessage, encodeSyncMessage, decodeSyncMessage, encodeSyncState, decodeSyncState, initSyncState } = require('./sync') - -module.exports = { - init, clone, free, applyChanges, applyLocalChange, save, load, loadChanges, getPatch, - getHeads, getAllChanges, getChanges, getChangesAdded, getChangeByHash, getMissingDeps, - receiveSyncMessage, generateSyncMessage, encodeSyncMessage, decodeSyncMessage, encodeSyncState, decodeSyncState, initSyncState -} - -},{"./backend":46,"./sync":51}],50:[function(require,module,exports){ -const { parseOpId, copyObject } = require('../src/common') -const { COLUMN_TYPE, VALUE_TYPE, ACTIONS, OBJECT_TYPE, DOC_OPS_COLUMNS, CHANGE_COLUMNS, DOCUMENT_COLUMNS, - encoderByColumnId, decoderByColumnId, makeDecoders, decodeValue, - encodeChange, decodeChangeColumns, decodeChangeMeta, decodeChanges, decodeDocumentHeader, encodeDocumentHeader } = require('./columnar') - -const MAX_BLOCK_SIZE = 600 // operations -const BLOOM_BITS_PER_ENTRY = 10, BLOOM_NUM_PROBES = 7 // 1% false positive rate -const BLOOM_FILTER_SIZE = Math.floor(BLOOM_BITS_PER_ENTRY * MAX_BLOCK_SIZE / 8) // bytes - -const objActorIdx = 0, objCtrIdx = 1, keyActorIdx = 2, keyCtrIdx = 3, keyStrIdx = 4, - idActorIdx = 5, idCtrIdx = 6, insertIdx = 7, actionIdx = 8, valLenIdx = 9, valRawIdx = 10, - predNumIdx = 13, predActorIdx = 14, predCtrIdx = 15, succNumIdx = 13, succActorIdx = 14, succCtrIdx = 15 - -const PRED_COLUMN_IDS = CHANGE_COLUMNS - .filter(column => ['predNum', 'predActor', 'predCtr'].includes(column.columnName)) - .map(column => column.columnId) - -/** - * Updates `objectTree`, which is a tree of nested objects, so that afterwards - * `objectTree[path[0]][path[1]][...] === value`. Only the root object is mutated, whereas any - * nested objects are copied before updating. This means that once the root object has been - * shallow-copied, this function can be used to update it without mutating the previous version. - */ -function deepCopyUpdate(objectTree, path, value) { - if (path.length === 1) { - objectTree[path[0]] = value - } else { - let child = Object.assign({}, objectTree[path[0]]) - deepCopyUpdate(child, path.slice(1), value) - objectTree[path[0]] = child - } -} - -/** - * Scans a block of document operations, encoded as columns `docCols`, to find the position at which - * an operation (or sequence of operations) `ops` should be applied. `actorIds` is the array that - * maps actor numbers to hexadecimal actor IDs. `resumeInsertion` is true if we're performing a list - * insertion and we already found the reference element in a previous block, but we reached the end - * of that previous block while scanning for the actual insertion position, and so we're continuing - * the scan in a subsequent block. - * - * Returns an object with keys: - * - `found`: false if we were scanning for a reference element in a list but couldn't find it; - * true otherwise. - * - `skipCount`: the number of operations, counted from the start of the block, after which the - * new operations should be inserted or applied. - * - `visibleCount`: if modifying a list object, the number of visible (i.e. non-deleted) list - * elements that precede the position where the new operations should be applied. - */ -function seekWithinBlock(ops, docCols, actorIds, resumeInsertion) { - for (let col of docCols) col.decoder.reset() - const { objActor, objCtr, keyActor, keyCtr, keyStr, idActor, idCtr, insert } = ops - const [objActorD, objCtrD, /* keyActorD */, /* keyCtrD */, keyStrD, idActorD, idCtrD, insertD, actionD, - /* valLenD */, /* valRawD */, /* chldActorD */, /* chldCtrD */, succNumD] = docCols.map(col => col.decoder) - let skipCount = 0, visibleCount = 0, elemVisible = false, nextObjActor = null, nextObjCtr = null - let nextIdActor = null, nextIdCtr = null, nextKeyStr = null, nextInsert = null, nextSuccNum = 0 - - // Seek to the beginning of the object being updated - if (objCtr !== null && !resumeInsertion) { - while (!objCtrD.done || !objActorD.done || !actionD.done) { - nextObjCtr = objCtrD.readValue() - nextObjActor = actorIds[objActorD.readValue()] - actionD.skipValues(1) - if (nextObjCtr === null || !nextObjActor || nextObjCtr < objCtr || - (nextObjCtr === objCtr && nextObjActor < objActor)) { - skipCount += 1 - } else { - break - } - } - } - if ((nextObjCtr !== objCtr || nextObjActor !== objActor) && !resumeInsertion) { - return {found: true, skipCount, visibleCount} - } - - // Seek to the appropriate key (if string key is used) - if (keyStr !== null) { - keyStrD.skipValues(skipCount) - while (!keyStrD.done) { - const objActorIndex = objActorD.readValue() - nextObjActor = objActorIndex === null ? null : actorIds[objActorIndex] - nextObjCtr = objCtrD.readValue() - nextKeyStr = keyStrD.readValue() - if (nextKeyStr !== null && nextKeyStr < keyStr && - nextObjCtr === objCtr && nextObjActor === objActor) { - skipCount += 1 - } else { - break - } - } - return {found: true, skipCount, visibleCount} - } - - idCtrD.skipValues(skipCount) - idActorD.skipValues(skipCount) - insertD.skipValues(skipCount) - succNumD.skipValues(skipCount) - nextIdCtr = idCtrD.readValue() - nextIdActor = actorIds[idActorD.readValue()] - nextInsert = insertD.readValue() - nextSuccNum = succNumD.readValue() - - // If we are inserting into a list, an opId key is used, and we need to seek to a position *after* - // the referenced operation. Moreover, we need to skip over any existing operations with a greater - // opId than the new insertion, for CRDT convergence on concurrent insertions in the same place. - if (insert) { - // If insertion is not at the head, search for the reference element - if (!resumeInsertion && keyCtr !== null && keyCtr > 0 && keyActor !== null) { - skipCount += 1 - while (!idCtrD.done && !idActorD.done && (nextIdCtr !== keyCtr || nextIdActor !== keyActor)) { - if (nextInsert) elemVisible = false - if (nextSuccNum === 0 && !elemVisible) { - visibleCount += 1 - elemVisible = true - } - nextIdCtr = idCtrD.readValue() - nextIdActor = actorIds[idActorD.readValue()] - nextObjCtr = objCtrD.readValue() - nextObjActor = actorIds[objActorD.readValue()] - nextInsert = insertD.readValue() - nextSuccNum = succNumD.readValue() - if (nextObjCtr === objCtr && nextObjActor === objActor) skipCount += 1; else break - } - if (nextObjCtr !== objCtr || nextObjActor !== objActor || nextIdCtr !== keyCtr || - nextIdActor !== keyActor || !nextInsert) { - return {found: false, skipCount, visibleCount} - } - if (nextInsert) elemVisible = false - if (nextSuccNum === 0 && !elemVisible) { - visibleCount += 1 - elemVisible = true - } - - // Set up the next* variables to the operation following the reference element - if (idCtrD.done || idActorD.done) return {found: true, skipCount, visibleCount} - nextIdCtr = idCtrD.readValue() - nextIdActor = actorIds[idActorD.readValue()] - nextObjCtr = objCtrD.readValue() - nextObjActor = actorIds[objActorD.readValue()] - nextInsert = insertD.readValue() - nextSuccNum = succNumD.readValue() - } - - // Skip over any list elements with greater ID than the new one, and any non-insertions - while ((!nextInsert || nextIdCtr > idCtr || (nextIdCtr === idCtr && nextIdActor > idActor)) && - nextObjCtr === objCtr && nextObjActor === objActor) { - skipCount += 1 - if (nextInsert) elemVisible = false - if (nextSuccNum === 0 && !elemVisible) { - visibleCount += 1 - elemVisible = true - } - if (!idCtrD.done && !idActorD.done) { - nextIdCtr = idCtrD.readValue() - nextIdActor = actorIds[idActorD.readValue()] - nextObjCtr = objCtrD.readValue() - nextObjActor = actorIds[objActorD.readValue()] - nextInsert = insertD.readValue() - nextSuccNum = succNumD.readValue() - } else { - break - } - } - - } else if (keyCtr !== null && keyCtr > 0 && keyActor !== null) { - // If we are updating an existing list element, seek to just before the referenced ID - while ((!nextInsert || nextIdCtr !== keyCtr || nextIdActor !== keyActor) && - nextObjCtr === objCtr && nextObjActor === objActor) { - skipCount += 1 - if (nextInsert) elemVisible = false - if (nextSuccNum === 0 && !elemVisible) { - visibleCount += 1 - elemVisible = true - } - if (!idCtrD.done && !idActorD.done) { - nextIdCtr = idCtrD.readValue() - nextIdActor = actorIds[idActorD.readValue()] - nextObjCtr = objCtrD.readValue() - nextObjActor = actorIds[objActorD.readValue()] - nextInsert = insertD.readValue() - nextSuccNum = succNumD.readValue() - } else { - break - } - } - if (nextObjCtr !== objCtr || nextObjActor !== objActor || nextIdCtr !== keyCtr || - nextIdActor !== keyActor || !nextInsert) { - return {found: false, skipCount, visibleCount} - } - } - return {found: true, skipCount, visibleCount} -} - -/** - * Returns the number of list elements that should be added to a list index when skipping over the - * block with index `blockIndex` in the list object with ID `objectId`. - */ -function visibleListElements(docState, blockIndex, objectId) { - const thisBlock = docState.blocks[blockIndex] - const nextBlock = docState.blocks[blockIndex + 1] - - let blockVisible = thisBlock.numVisible[objectId] - if (blockVisible !== undefined) { - // If a list element is split across the block boundary, don't double-count it - if (thisBlock.lastVisibleActor === nextBlock.firstVisibleActor && - thisBlock.lastVisibleActor !== undefined && - thisBlock.lastVisibleCtr === nextBlock.firstVisibleCtr && - thisBlock.lastVisibleCtr !== undefined) blockVisible -= 1 - return blockVisible - } else { - return 0 - } -} - -/** - * Scans the blocks of document operations to find the position where a new operation should be - * inserted. Returns an object with keys: - * - `blockIndex`: the index of the block into which we should insert the new operation - * - `skipCount`: the number of operations, counted from the start of the block, after which the - * new operations should be inserted or merged. - * - `visibleCount`: if modifying a list object, the number of visible (i.e. non-deleted) list - * elements that precede the position where the new operations should be applied. - */ -function seekToOp(docState, ops) { - const { objActor, objCtr, keyActor, keyCtr, keyStr } = ops - let blockIndex = 0, totalVisible = 0 - - // Skip any blocks that contain only objects with lower objectIds - if (objCtr !== null) { - while (blockIndex < docState.blocks.length - 1) { - const blockActor = docState.blocks[blockIndex].lastObjectActor === undefined ? undefined - : docState.actorIds[docState.blocks[blockIndex].lastObjectActor] - const blockCtr = docState.blocks[blockIndex].lastObjectCtr - if (blockCtr === undefined || blockCtr < objCtr || (blockCtr === objCtr && blockActor < objActor)) { - blockIndex++ - } else { - break - } - } - } - - if (keyStr !== null) { - // String key is used. First skip any blocks that contain only lower keys - while (blockIndex < docState.blocks.length - 1) { - const blockLastKey = docState.blocks[blockIndex].lastKey[ops.objId] - if (blockLastKey !== undefined && blockLastKey < keyStr) blockIndex++; else break - } - - // When we have a candidate block, decode it to find the exact insertion position - const {skipCount} = seekWithinBlock(ops, docState.blocks[blockIndex].columns, docState.actorIds, false) - return {blockIndex, skipCount, visibleCount: 0} - - } else { - // List operation - const insertAtHead = keyCtr === null || keyCtr === 0 || keyActor === null - const keyActorNum = keyActor === null ? null : docState.actorIds.indexOf(keyActor) - let resumeInsertion = false - - while (true) { - // Search for the reference element, skipping any blocks whose Bloom filter does not contain - // the reference element. We only do this if not inserting at the head (in which case there is - // no reference element), or if we already found the reference element in an earlier block (in - // which case we have resumeInsertion === true). The latter case arises with concurrent - // insertions at the same position, and so we have to scan beyond the reference element to - // find the actual insertion position, and that further scan crosses a block boundary. - if (!insertAtHead && !resumeInsertion) { - while (blockIndex < docState.blocks.length - 1 && - !bloomFilterContains(docState.blocks[blockIndex].bloom, keyActorNum, keyCtr)) { - // If we reach the end of the list object without a Bloom filter hit, the reference element - // doesn't exist - if (docState.blocks[blockIndex].lastObjectCtr > objCtr) { - throw new RangeError(`Reference element not found: ${keyCtr}@${keyActor}`) - } - - // Add up number of visible list elements in any blocks we skip, for list index computation - totalVisible += visibleListElements(docState, blockIndex, ops.objId) - blockIndex++ - } - } - - // We have a candidate block. Decode it to see whether it really contains the reference element - const {found, skipCount, visibleCount} = seekWithinBlock(ops, - docState.blocks[blockIndex].columns, - docState.actorIds, - resumeInsertion) - - if (blockIndex === docState.blocks.length - 1) { - // Last block: if we haven't found the reference element by now, it's an error - if (found) { - return {blockIndex, skipCount, visibleCount: totalVisible + visibleCount} - } else { - throw new RangeError(`Reference element not found: ${keyCtr}@${keyActor}`) - } - - } else if (found && skipCount < docState.blocks[blockIndex].numOps) { - // The insertion position lies within the current block - return {blockIndex, skipCount, visibleCount: totalVisible + visibleCount} - } - - // Reference element not found and there are still blocks left ==> it was probably a false positive. - // Reference element found, but we skipped all the way to the end of the block ==> we need to - // continue scanning the next block to find the actual insertion position. - // Either way, go back round the loop again to skip blocks until the next Bloom filter hit. - resumeInsertion = found && ops.insert - totalVisible += visibleListElements(docState, blockIndex, ops.objId) - blockIndex++ - } - } -} - -/** - * Updates Bloom filter `bloom`, given as a Uint8Array, to contain the list element ID consisting of - * counter `elemIdCtr` and actor number `elemIdActor`. We don't actually bother computing a hash - * function, since those two integers serve perfectly fine as input. We turn the two integers into a - * sequence of probe indexes using the triple hashing algorithm from the following paper: - * - * Peter C. Dillinger and Panagiotis Manolios. Bloom Filters in Probabilistic Verification. - * 5th International Conference on Formal Methods in Computer-Aided Design (FMCAD), November 2004. - * http://www.ccis.northeastern.edu/home/pete/pub/bloom-filters-verification.pdf - */ -function bloomFilterAdd(bloom, elemIdActor, elemIdCtr) { - let modulo = 8 * bloom.byteLength, x = elemIdCtr % modulo, y = elemIdActor % modulo - - // Use one step of FNV-1a to compute a third value from the two inputs. - // Taken from http://www.isthe.com/chongo/tech/comp/fnv/index.html - // The prime is just over 2^24, so elemIdCtr can be up to about 2^29 = 500 million before the - // result of the multiplication exceeds 2^53. And even if it does exceed 2^53 and loses precision, - // that shouldn't be a problem as it should still be deterministic, and the Bloom filter - // computation only needs to be internally consistent within this library. - let z = ((elemIdCtr ^ elemIdActor) * 16777619 >>> 0) % modulo - - for (let i = 0; i < BLOOM_NUM_PROBES; i++) { - bloom[x >>> 3] |= 1 << (x & 7) - x = (x + y) % modulo - y = (y + z) % modulo - } -} - -/** - * Returns true if the list element ID consisting of counter `elemIdCtr` and actor number - * `elemIdActor` is likely to be contained in the Bloom filter `bloom`. - */ -function bloomFilterContains(bloom, elemIdActor, elemIdCtr) { - let modulo = 8 * bloom.byteLength, x = elemIdCtr % modulo, y = elemIdActor % modulo - let z = ((elemIdCtr ^ elemIdActor) * 16777619 >>> 0) % modulo - - // See comments in the bloomFilterAdd function for an explanation - for (let i = 0; i < BLOOM_NUM_PROBES; i++) { - if ((bloom[x >>> 3] & (1 << (x & 7))) === 0) { - return false - } - x = (x + y) % modulo - y = (y + z) % modulo - } - return true -} - -/** - * Reads the relevant columns of a block of operations and updates that block to contain the - * metadata we need to efficiently figure out where to insert new operations. - */ -function updateBlockMetadata(block, actorIds) { - block.bloom = new Uint8Array(BLOOM_FILTER_SIZE) - block.lastKey = {} - block.numVisible = {} - block.numOps = 0 - block.lastObjectActor = undefined - block.lastObjectCtr = undefined - block.firstVisibleActor = undefined - block.firstVisibleCtr = undefined - block.lastVisibleActor = undefined - block.lastVisibleCtr = undefined - - for (let col of block.columns) col.decoder.reset() - const [objActorD, objCtrD, keyActorD, keyCtrD, keyStrD, idActorD, idCtrD, insertD, /* actionD */, - /* valLenD */, /* valRawD */, /* chldActorD */, /* chldCtrD */, succNumD] = block.columns.map(col => col.decoder) - - while (!idCtrD.done) { - block.numOps += 1 - const objActor = objActorD.readValue(), objCtr = objCtrD.readValue() - const keyActor = keyActorD.readValue(), keyCtr = keyCtrD.readValue(), keyStr = keyStrD.readValue() - const idActor = idActorD.readValue(), idCtr = idCtrD.readValue() - const insert = insertD.readValue(), succNum = succNumD.readValue() - const objectId = objActor === null ? '_root' : `${objCtr}@${actorIds[objActor]}` - - if (objActor !== null && objCtr !== null) { - block.lastObjectActor = objActor - block.lastObjectCtr = objCtr - } - - if (keyStr !== null) { - // Map key: for each object, record the highest key contained in the block - block.lastKey[objectId] = keyStr - } else if (insert || keyCtr !== null) { - // List element - if (block.numVisible[objectId] === undefined) block.numVisible[objectId] = 0 - const elemIdActor = insert ? idActor : keyActor - const elemIdCtr = insert ? idCtr : keyCtr - bloomFilterAdd(block.bloom, elemIdActor, elemIdCtr) - - // If the list element is visible, update the block metadata accordingly - if (succNum === 0) { - if (block.firstVisibleActor === undefined) block.firstVisibleActor = elemIdActor - if (block.firstVisibleCtr === undefined) block.firstVisibleCtr = elemIdCtr - if (block.lastVisibleActor !== elemIdActor || block.lastVisibleCtr !== elemIdCtr) { - block.numVisible[objectId] += 1 - } - block.lastVisibleActor = elemIdActor - block.lastVisibleCtr = elemIdCtr - } - } - } -} - -/** - * Updates a block's metadata based on an operation being added to a block. - */ -function addBlockOperation(block, op, objectId, actorIds, isChangeOp) { - // Keep track of the largest objectId contained within a block - if (op[objActorIdx] !== null && op[objCtrIdx] !== null && - (block.lastObjectCtr === undefined || block.lastObjectCtr < op[objCtrIdx] || - (block.lastObjectCtr === op[objCtrIdx] && actorIds[block.lastObjectActor] < actorIds[op[objActorIdx]]))) { - block.lastObjectActor = op[objActorIdx] - block.lastObjectCtr = op[objCtrIdx] - } - - if (op[keyStrIdx] !== null) { - // TODO this comparison should use UTF-8 encoding, not JavaScript's UTF-16 - if (block.lastKey[objectId] === undefined || block.lastKey[objectId] < op[keyStrIdx]) { - block.lastKey[objectId] = op[keyStrIdx] - } - } else { - // List element - const elemIdActor = op[insertIdx] ? op[idActorIdx] : op[keyActorIdx] - const elemIdCtr = op[insertIdx] ? op[idCtrIdx] : op[keyCtrIdx] - bloomFilterAdd(block.bloom, elemIdActor, elemIdCtr) - - if (op[succNumIdx] === 0 || isChangeOp) { - if (block.firstVisibleActor === undefined) block.firstVisibleActor = elemIdActor - if (block.firstVisibleCtr === undefined) block.firstVisibleCtr = elemIdCtr - block.lastVisibleActor = elemIdActor - block.lastVisibleCtr = elemIdCtr - } - } -} - -/** - * Takes a block containing too many operations, and splits it into a sequence of adjacent blocks of - * roughly equal size. - */ -function splitBlock(block, actorIds) { - for (let col of block.columns) col.decoder.reset() - - // Make each of the resulting blocks between 50% and 80% full (leaving a bit of space in each - // block so that it doesn't get split again right away the next time an operation is added). - // The upper bound cannot be lower than 75% since otherwise we would end up with a block less than - // 50% full when going from two to three blocks. - const numBlocks = Math.ceil(block.numOps / (0.8 * MAX_BLOCK_SIZE)) - let blocks = [], opsSoFar = 0 - - for (let i = 1; i <= numBlocks; i++) { - const opsToCopy = Math.ceil(i * block.numOps / numBlocks) - opsSoFar - const encoders = block.columns.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) - copyColumns(encoders, block.columns, opsToCopy) - const decoders = encoders.map(col => { - const decoder = decoderByColumnId(col.columnId, col.encoder.buffer) - return {columnId: col.columnId, decoder} - }) - - const newBlock = {columns: decoders} - updateBlockMetadata(newBlock, actorIds) - blocks.push(newBlock) - opsSoFar += opsToCopy - } - - return blocks -} - -/** - * Takes an array of blocks and concatenates the corresponding columns across all of the blocks. - */ -function concatBlocks(blocks) { - const encoders = blocks[0].columns.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) - - for (let block of blocks) { - for (let col of block.columns) col.decoder.reset() - copyColumns(encoders, block.columns, block.numOps) - } - return encoders -} - -/** - * Copies `count` rows from the set of input columns `inCols` to the set of output columns - * `outCols`. The input columns are given as an array of `{columnId, decoder}` objects, and the - * output columns are given as an array of `{columnId, encoder}` objects. Both are sorted in - * increasing order of columnId. If there is no matching input column for a given output column, it - * is filled in with `count` blank values (according to the column type). - */ -function copyColumns(outCols, inCols, count) { - if (count === 0) return - let inIndex = 0, lastGroup = -1, lastCardinality = 0, valueColumn = -1, valueBytes = 0 - for (let outCol of outCols) { - while (inIndex < inCols.length && inCols[inIndex].columnId < outCol.columnId) inIndex++ - let inCol = null - if (inIndex < inCols.length && inCols[inIndex].columnId === outCol.columnId && - inCols[inIndex].decoder.buf.byteLength > 0) { - inCol = inCols[inIndex].decoder - } - const colCount = (outCol.columnId >> 4 === lastGroup) ? lastCardinality : count - - if (outCol.columnId % 8 === COLUMN_TYPE.GROUP_CARD) { - lastGroup = outCol.columnId >> 4 - if (inCol) { - lastCardinality = outCol.encoder.copyFrom(inCol, {count, sumValues: true}).sum - } else { - outCol.encoder.appendValue(0, count) - lastCardinality = 0 - } - } else if (outCol.columnId % 8 === COLUMN_TYPE.VALUE_LEN) { - if (inCol) { - if (inIndex + 1 === inCols.length || inCols[inIndex + 1].columnId !== outCol.columnId + 1) { - throw new RangeError('VALUE_LEN column without accompanying VALUE_RAW column') - } - valueColumn = outCol.columnId + 1 - valueBytes = outCol.encoder.copyFrom(inCol, {count: colCount, sumValues: true, sumShift: 4}).sum - } else { - outCol.encoder.appendValue(null, colCount) - valueColumn = outCol.columnId + 1 - valueBytes = 0 - } - } else if (outCol.columnId % 8 === COLUMN_TYPE.VALUE_RAW) { - if (outCol.columnId !== valueColumn) { - throw new RangeError('VALUE_RAW column without accompanying VALUE_LEN column') - } - if (valueBytes > 0) { - outCol.encoder.appendRawBytes(inCol.readRawBytes(valueBytes)) - } - } else { // ACTOR_ID, INT_RLE, INT_DELTA, BOOLEAN, or STRING_RLE - if (inCol) { - outCol.encoder.copyFrom(inCol, {count: colCount}) - } else { - const blankValue = (outCol.columnId % 8 === COLUMN_TYPE.BOOLEAN) ? false : null - outCol.encoder.appendValue(blankValue, colCount) - } - } - } -} - -/** - * Parses one operation from a set of columns. The argument `columns` contains a list of objects - * with `columnId` and `decoder` properties. Returns an array in which the i'th element is the - * value read from the i'th column in `columns`. Does not interpret datatypes; the only - * interpretation of values is that if `actorTable` is given, a value `v` in a column of type - * ACTOR_ID is replaced with `actorTable[v]`. - */ -function readOperation(columns, actorTable) { - let operation = [], colValue, lastGroup = -1, lastCardinality = 0, valueColumn = -1, valueBytes = 0 - for (let col of columns) { - if (col.columnId % 8 === COLUMN_TYPE.VALUE_RAW) { - if (col.columnId !== valueColumn) throw new RangeError('unexpected VALUE_RAW column') - colValue = col.decoder.readRawBytes(valueBytes) - } else if (col.columnId % 8 === COLUMN_TYPE.GROUP_CARD) { - lastGroup = col.columnId >> 4 - lastCardinality = col.decoder.readValue() || 0 - colValue = lastCardinality - } else if (col.columnId >> 4 === lastGroup) { - colValue = [] - if (col.columnId % 8 === COLUMN_TYPE.VALUE_LEN) { - valueColumn = col.columnId + 1 - valueBytes = 0 - } - for (let i = 0; i < lastCardinality; i++) { - let value = col.decoder.readValue() - if (col.columnId % 8 === COLUMN_TYPE.ACTOR_ID && actorTable && typeof value === 'number') { - value = actorTable[value] - } - if (col.columnId % 8 === COLUMN_TYPE.VALUE_LEN) { - valueBytes += colValue >>> 4 - } - colValue.push(value) - } - } else { - colValue = col.decoder.readValue() - if (col.columnId % 8 === COLUMN_TYPE.ACTOR_ID && actorTable && typeof colValue === 'number') { - colValue = actorTable[colValue] - } - if (col.columnId % 8 === COLUMN_TYPE.VALUE_LEN) { - valueColumn = col.columnId + 1 - valueBytes = colValue >>> 4 - } - } - - operation.push(colValue) - } - return operation -} - -/** - * Appends `operation`, in the form returned by `readOperation()`, to the columns in `outCols`. The - * argument `inCols` provides metadata about the types of columns in `operation`; the value - * `operation[i]` comes from the column `inCols[i]`. - */ -function appendOperation(outCols, inCols, operation) { - let inIndex = 0, lastGroup = -1, lastCardinality = 0 - for (let outCol of outCols) { - while (inIndex < inCols.length && inCols[inIndex].columnId < outCol.columnId) inIndex++ - - if (inIndex < inCols.length && inCols[inIndex].columnId === outCol.columnId) { - const colValue = operation[inIndex] - if (outCol.columnId % 8 === COLUMN_TYPE.GROUP_CARD) { - lastGroup = outCol.columnId >> 4 - lastCardinality = colValue - outCol.encoder.appendValue(colValue) - } else if (outCol.columnId >> 4 === lastGroup) { - if (!Array.isArray(colValue) || colValue.length !== lastCardinality) { - throw new RangeError('bad group value') - } - for (let v of colValue) outCol.encoder.appendValue(v) - } else if (outCol.columnId % 8 === COLUMN_TYPE.VALUE_RAW) { - if (colValue) outCol.encoder.appendRawBytes(colValue) - } else { - outCol.encoder.appendValue(colValue) - } - } else if (outCol.columnId % 8 === COLUMN_TYPE.GROUP_CARD) { - lastGroup = outCol.columnId >> 4 - lastCardinality = 0 - outCol.encoder.appendValue(0) - } else if (outCol.columnId % 8 !== COLUMN_TYPE.VALUE_RAW) { - const count = (outCol.columnId >> 4 === lastGroup) ? lastCardinality : 1 - let blankValue = null - if (outCol.columnId % 8 === COLUMN_TYPE.BOOLEAN) blankValue = false - if (outCol.columnId % 8 === COLUMN_TYPE.VALUE_LEN) blankValue = 0 - outCol.encoder.appendValue(blankValue, count) - } - } -} - -/** - * Parses the next operation from block `blockIndex` of the document. Returns an object of the form - * `{docOp, blockIndex}` where `docOp` is an operation in the form returned by `readOperation()`, - * and `blockIndex` is the block number to use on the next call (it moves on to the next block when - * we reach the end of the current block). `docOp` is null if there are no more operations. - */ -function readNextDocOp(docState, blockIndex) { - let block = docState.blocks[blockIndex] - if (!block.columns[actionIdx].decoder.done) { - return {docOp: readOperation(block.columns), blockIndex} - } else if (blockIndex === docState.blocks.length - 1) { - return {docOp: null, blockIndex} - } else { - blockIndex += 1 - block = docState.blocks[blockIndex] - for (let col of block.columns) col.decoder.reset() - return {docOp: readOperation(block.columns), blockIndex} - } -} - -/** - * Parses the next operation from a sequence of changes. `changeState` serves as the state of this - * pseudo-iterator, and it is mutated to reflect the new operation. In particular, - * `changeState.nextOp` is set to the operation that was read, and `changeState.done` is set to true - * when we have finished reading the last operation in the last change. - */ -function readNextChangeOp(docState, changeState) { - // If we've finished reading one change, move to the next change that contains at least one op - while (changeState.changeIndex < changeState.changes.length - 1 && - (!changeState.columns || changeState.columns[actionIdx].decoder.done)) { - changeState.changeIndex += 1 - const change = changeState.changes[changeState.changeIndex] - changeState.columns = makeDecoders(change.columns, CHANGE_COLUMNS) - changeState.opCtr = change.startOp - - // Update docState based on the information in the change - updateBlockColumns(docState, changeState.columns) - const {actorIds, actorTable} = getActorTable(docState.actorIds, change) - docState.actorIds = actorIds - changeState.actorTable = actorTable - changeState.actorIndex = docState.actorIds.indexOf(change.actorIds[0]) - } - - // Reached the end of the last change? - if (changeState.columns[actionIdx].decoder.done) { - changeState.done = true - changeState.nextOp = null - return - } - - changeState.nextOp = readOperation(changeState.columns, changeState.actorTable) - changeState.nextOp[idActorIdx] = changeState.actorIndex - changeState.nextOp[idCtrIdx] = changeState.opCtr - changeState.changes[changeState.changeIndex].maxOp = changeState.opCtr - if (changeState.opCtr > docState.maxOp) docState.maxOp = changeState.opCtr - changeState.opCtr += 1 - - const op = changeState.nextOp - if ((op[objCtrIdx] === null && op[objActorIdx] !== null) || - (op[objCtrIdx] !== null && op[objActorIdx] === null)) { - throw new RangeError(`Mismatched object reference: (${op[objCtrIdx]}, ${op[objActorIdx]})`) - } - if ((op[keyCtrIdx] === null && op[keyActorIdx] !== null) || - (op[keyCtrIdx] === 0 && op[keyActorIdx] !== null) || - (op[keyCtrIdx] > 0 && op[keyActorIdx] === null)) { - throw new RangeError(`Mismatched operation key: (${op[keyCtrIdx]}, ${op[keyActorIdx]})`) - } -} - -function emptyObjectPatch(objectId, type) { - if (type === 'list' || type === 'text') { - return {objectId, type, edits: []} - } else { - return {objectId, type, props: {}} - } -} - -/** - * Returns true if the two given operation IDs have the same actor ID, and the counter of `id2` is - * exactly `delta` greater than the counter of `id1`. - */ -function opIdDelta(id1, id2, delta = 1) { - const parsed1 = parseOpId(id1), parsed2 = parseOpId(id2) - return parsed1.actorId === parsed2.actorId && parsed1.counter + delta === parsed2.counter -} - -/** - * Appends a list edit operation (insert, update, remove) to an array of existing operations. If the - * last existing operation can be extended (as a multi-op), we do that. - */ -function appendEdit(existingEdits, nextEdit) { - if (existingEdits.length === 0) { - existingEdits.push(nextEdit) - return - } - - let lastEdit = existingEdits[existingEdits.length - 1] - if (lastEdit.action === 'insert' && nextEdit.action === 'insert' && - lastEdit.index === nextEdit.index - 1 && - lastEdit.value.type === 'value' && nextEdit.value.type === 'value' && - lastEdit.elemId === lastEdit.opId && nextEdit.elemId === nextEdit.opId && - opIdDelta(lastEdit.elemId, nextEdit.elemId, 1) && - lastEdit.value.datatype === nextEdit.value.datatype && - typeof lastEdit.value.value === typeof nextEdit.value.value) { - lastEdit.action = 'multi-insert' - if (nextEdit.value.datatype) lastEdit.datatype = nextEdit.value.datatype - lastEdit.values = [lastEdit.value.value, nextEdit.value.value] - delete lastEdit.value - delete lastEdit.opId - - } else if (lastEdit.action === 'multi-insert' && nextEdit.action === 'insert' && - lastEdit.index + lastEdit.values.length === nextEdit.index && - nextEdit.value.type === 'value' && nextEdit.elemId === nextEdit.opId && - opIdDelta(lastEdit.elemId, nextEdit.elemId, lastEdit.values.length) && - lastEdit.datatype === nextEdit.value.datatype && - typeof lastEdit.values[0] === typeof nextEdit.value.value) { - lastEdit.values.push(nextEdit.value.value) - - } else if (lastEdit.action === 'remove' && nextEdit.action === 'remove' && - lastEdit.index === nextEdit.index) { - lastEdit.count += nextEdit.count - - } else { - existingEdits.push(nextEdit) - } -} - -/** - * `edits` is an array of (SingleInsertEdit | MultiInsertEdit | UpdateEdit | RemoveEdit) list edits - * for a patch. This function appends an UpdateEdit to this array. A conflict is represented by - * having several consecutive edits with the same index, and this can be realised by calling - * `appendUpdate` several times for the same list element. On the first such call, `firstUpdate` - * must be true. - * - * It is possible that coincidentally the previous edit (potentially arising from a different - * change) is for the same index. If this is the case, to avoid accidentally treating consecutive - * updates for the same index as a conflict, we remove the previous edit for the same index. This is - * safe because the previous edit is overwritten by the new edit being appended, and we know that - * it's for the same list elements because there are no intervening insertions/deletions that could - * have changed the indexes. - */ -function appendUpdate(edits, index, elemId, opId, value, firstUpdate) { - let insert = false - if (firstUpdate) { - // Pop all edits for the same index off the end of the edits array. This sequence may begin with - // either an insert or an update. If it's an insert, we remember that fact, and use it below. - while (!insert && edits.length > 0) { - const lastEdit = edits[edits.length - 1] - if ((lastEdit.action === 'insert' || lastEdit.action === 'update') && lastEdit.index === index) { - edits.pop() - insert = (lastEdit.action === 'insert') - } else if (lastEdit.action === 'multi-insert' && lastEdit.index + lastEdit.values.length - 1 === index) { - lastEdit.values.pop() - insert = true - } else { - break - } - } - } - - // If we popped an insert edit off the edits array, we need to turn the new update into an insert - // in order to ensure the list element still gets inserted (just with a new value). - if (insert) { - appendEdit(edits, {action: 'insert', index, elemId, opId, value}) - } else { - appendEdit(edits, {action: 'update', index, opId, value}) - } -} - -/** - * `edits` is an array of (SingleInsertEdit | MultiInsertEdit | UpdateEdit | RemoveEdit) list edits - * for a patch. We assume that there is a suffix of this array that consists of an insertion at - * position `index`, followed by zero or more UpdateEdits at the same index. This function rewrites - * that suffix to be all updates instead. This is needed because sometimes when generating a patch - * we think we are performing a list insertion, but then it later turns out that there was already - * an existing value at that list element, and so we actually need to do an update, not an insert. - * - * If the suffix is preceded by one or more updates at the same index, those earlier updates are - * removed by `appendUpdate()` to ensure we don't inadvertently treat them as part of the same - * conflict. - */ -function convertInsertToUpdate(edits, index, elemId) { - let updates = [] - while (edits.length > 0) { - let lastEdit = edits[edits.length - 1] - if (lastEdit.action === 'insert') { - if (lastEdit.index !== index) throw new RangeError('last edit has unexpected index') - updates.unshift(edits.pop()) - break - } else if (lastEdit.action === 'update') { - if (lastEdit.index !== index) throw new RangeError('last edit has unexpected index') - updates.unshift(edits.pop()) - } else { - // It's impossible to encounter a remove edit here because the state machine in - // updatePatchProperty() ensures that a property can have either an insert or a remove edit, - // but not both. It's impossible to encounter a multi-insert here because multi-inserts always - // have equal elemId and opId (i.e. they can only be used for the operation that first inserts - // an element, but not for any subsequent assignments to that list element); moreover, - // convertInsertToUpdate is only called if an insert action is followed by a non-overwritten - // document op. The fact that there is a non-overwritten document op after another op on the - // same list element implies that the original insertion op for that list element must be - // overwritten, and thus the original insertion op cannot have given rise to a multi-insert. - throw new RangeError('last edit has unexpected action') - } - } - - // Now take the edits we popped off and push them back onto the list again - let firstUpdate = true - for (let update of updates) { - appendUpdate(edits, index, elemId, update.opId, update.value, firstUpdate) - firstUpdate = false - } -} - -/** - * Updates `patches` to reflect the operation `op` within the document with state `docState`. - * Can be called multiple times if there are multiple operations for the same property (e.g. due - * to a conflict). `propState` is an object that carries over state between such successive - * invocations for the same property. If the current object is a list, `listIndex` is the index - * into that list (counting only visible elements). If the operation `op` was already previously - * in the document, `oldSuccNum` is the value of `op[succNumIdx]` before the current change was - * applied (allowing us to determine whether this operation was overwritten or deleted in the - * current change). `oldSuccNum` must be undefined if the operation came from the current change. - * If we are creating an incremental patch as a result of applying one or more changes, `newBlock` - * is the block to which the operations are getting written; we will update the metadata on this - * block. `newBlock` should be null if we are creating a patch for the whole document. - */ -function updatePatchProperty(patches, newBlock, objectId, op, docState, propState, listIndex, oldSuccNum) { - const isWholeDoc = !newBlock - const type = op[actionIdx] < ACTIONS.length ? OBJECT_TYPE[ACTIONS[op[actionIdx]]] : null - const opId = `${op[idCtrIdx]}@${docState.actorIds[op[idActorIdx]]}` - const elemIdActor = op[insertIdx] ? op[idActorIdx] : op[keyActorIdx] - const elemIdCtr = op[insertIdx] ? op[idCtrIdx] : op[keyCtrIdx] - const elemId = op[keyStrIdx] ? op[keyStrIdx] : `${elemIdCtr}@${docState.actorIds[elemIdActor]}` - - // When the change contains a new make* operation (i.e. with an even-numbered action), record the - // new parent-child relationship in objectMeta. TODO: also handle link/move operations. - if (op[actionIdx] % 2 === 0 && !docState.objectMeta[opId]) { - docState.objectMeta[opId] = {parentObj: objectId, parentKey: elemId, opId, type, children: {}} - deepCopyUpdate(docState.objectMeta, [objectId, 'children', elemId, opId], {objectId: opId, type, props: {}}) - } - - // firstOp is true if the current operation is the first of a sequence of ops for the same key - const firstOp = !propState[elemId] - if (!propState[elemId]) propState[elemId] = {visibleOps: [], hasChild: false} - - // An operation is overwritten if it is a document operation that has at least one successor - const isOverwritten = (oldSuccNum !== undefined && op[succNumIdx] > 0) - - // Record all visible values for the property, and whether it has any child object - if (!isOverwritten) { - propState[elemId].visibleOps.push(op) - propState[elemId].hasChild = propState[elemId].hasChild || (op[actionIdx] % 2) === 0 // even-numbered action == make* operation - } - - // If one or more of the values of the property is a child object, we update objectMeta to store - // all of the visible values of the property (even the non-child-object values). Then, when we - // subsequently process an update within that child object, we can construct the patch to - // contain the conflicting values. - const prevChildren = docState.objectMeta[objectId].children[elemId] - if (propState[elemId].hasChild || (prevChildren && Object.keys(prevChildren).length > 0)) { - let values = {} - for (let visible of propState[elemId].visibleOps) { - const opId = `${visible[idCtrIdx]}@${docState.actorIds[visible[idActorIdx]]}` - if (ACTIONS[visible[actionIdx]] === 'set') { - values[opId] = Object.assign({type: 'value'}, decodeValue(visible[valLenIdx], visible[valRawIdx])) - } else if (visible[actionIdx] % 2 === 0) { - const objType = visible[actionIdx] < ACTIONS.length ? OBJECT_TYPE[ACTIONS[visible[actionIdx]]] : null - values[opId] = emptyObjectPatch(opId, objType) - } - } - - // Copy so that objectMeta is not modified if an exception is thrown while applying change - deepCopyUpdate(docState.objectMeta, [objectId, 'children', elemId], values) - } - - let patchKey, patchValue - - // For counters, increment operations are succs to the set operation that created the counter, - // but in this case we want to add the values rather than overwriting them. - if (isOverwritten && ACTIONS[op[actionIdx]] === 'set' && (op[valLenIdx] & 0x0f) === VALUE_TYPE.COUNTER) { - // This is the initial set operation that creates a counter. Initialise the counter state - // to contain all successors of the set operation. Only if we later find that each of these - // successor operations is an increment, we make the counter visible in the patch. - if (!propState[elemId]) propState[elemId] = {visibleOps: [], hasChild: false} - if (!propState[elemId].counterStates) propState[elemId].counterStates = {} - let counterStates = propState[elemId].counterStates - let counterState = {opId, value: decodeValue(op[valLenIdx], op[valRawIdx]).value, succs: {}} - - for (let i = 0; i < op[succNumIdx]; i++) { - const succOp = `${op[succCtrIdx][i]}@${docState.actorIds[op[succActorIdx][i]]}` - counterStates[succOp] = counterState - counterState.succs[succOp] = true - } - - } else if (ACTIONS[op[actionIdx]] === 'inc') { - // Incrementing a previously created counter. - if (!propState[elemId] || !propState[elemId].counterStates || !propState[elemId].counterStates[opId]) { - throw new RangeError(`increment operation ${opId} for unknown counter`) - } - let counterState = propState[elemId].counterStates[opId] - counterState.value += decodeValue(op[valLenIdx], op[valRawIdx]).value - delete counterState.succs[opId] - - if (Object.keys(counterState.succs).length === 0) { - patchKey = counterState.opId - patchValue = {type: 'value', datatype: 'counter', value: counterState.value} - // TODO if the counter is in a list element, we need to add a 'remove' action when deleted - } - - } else if (!isOverwritten) { - // Add the value to the patch if it is not overwritten (i.e. if it has no succs). - if (ACTIONS[op[actionIdx]] === 'set') { - patchKey = opId - patchValue = Object.assign({type: 'value'}, decodeValue(op[valLenIdx], op[valRawIdx])) - } else if (op[actionIdx] % 2 === 0) { // even-numbered action == make* operation - if (!patches[opId]) patches[opId] = emptyObjectPatch(opId, type) - patchKey = opId - patchValue = patches[opId] - } - } - - if (!patches[objectId]) patches[objectId] = emptyObjectPatch(objectId, docState.objectMeta[objectId].type) - const patch = patches[objectId] - - // Updating a list or text object (with elemId key) - if (op[keyStrIdx] === null) { - // If we come across any document op that was previously non-overwritten/non-deleted, that - // means the current list element already had a value before this change was applied, and - // therefore the current element cannot be an insert. If we already registered an insert, we - // have to convert it into an update. - if (oldSuccNum === 0 && !isWholeDoc && propState[elemId].action === 'insert') { - propState[elemId].action = 'update' - convertInsertToUpdate(patch.edits, listIndex, elemId) - if (newBlock) newBlock.numVisible[objectId] -= 1 - } - - if (patchValue) { - // If the op has a non-overwritten value and it came from the change, it's an insert. - // (It's not necessarily the case that op[insertIdx] is true: if a list element is concurrently - // deleted and updated, the node that first processes the deletion and then the update will - // observe the update as a re-insertion of the deleted list element.) - if (!propState[elemId].action && (oldSuccNum === undefined || isWholeDoc)) { - propState[elemId].action = 'insert' - appendEdit(patch.edits, {action: 'insert', index: listIndex, elemId, opId: patchKey, value: patchValue}) - if (newBlock) { - if (newBlock.numVisible[objectId] === undefined) newBlock.numVisible[objectId] = 0 - newBlock.numVisible[objectId] += 1 - } - - // If the property has a value and it's not an insert, then it must be an update. - // We might have previously registered it as a remove, in which case we convert it to update. - } else if (propState[elemId].action === 'remove') { - let lastEdit = patch.edits[patch.edits.length - 1] - if (lastEdit.action !== 'remove') throw new RangeError('last edit has unexpected type') - if (lastEdit.count > 1) lastEdit.count -= 1; else patch.edits.pop() - propState[elemId].action = 'update' - appendUpdate(patch.edits, listIndex, elemId, patchKey, patchValue, true) - if (newBlock) newBlock.numVisible[objectId] += 1 - - } else { - // A 'normal' update - appendUpdate(patch.edits, listIndex, elemId, patchKey, patchValue, !propState[elemId].action) - if (!propState[elemId].action) propState[elemId].action = 'update' - } - - } else if (oldSuccNum === 0 && !propState[elemId].action) { - // If the property used to have a non-overwritten/non-deleted value, but no longer, it's a remove - propState[elemId].action = 'remove' - appendEdit(patch.edits, {action: 'remove', index: listIndex, count: 1}) - if (newBlock) newBlock.numVisible[objectId] -= 1 - } - - } else if (patchValue || !isWholeDoc) { - // Updating a map or table (with string key) - if (firstOp || !patch.props[op[keyStrIdx]]) patch.props[op[keyStrIdx]] = {} - if (patchValue) patch.props[op[keyStrIdx]][patchKey] = patchValue - } -} - -/** - * Applies operations (from one or more changes) to the document by merging the sequence of change - * ops into the sequence of document ops. The two inputs are `changeState` and `docState` - * respectively. Assumes that the decoders of both sets of columns are at the position where we want - * to start merging. `patches` is mutated to reflect the effect of the change operations. `ops` is - * the operation sequence to apply (as decoded by `groupRelatedOps()`). `docState` is as - * documented in `applyOps()`. If the operations are updating a list or text object, `listIndex` - * is the number of visible elements that precede the position at which we start merging. - * `blockIndex` is the document block number from which we are currently reading. - */ -function mergeDocChangeOps(patches, newBlock, outCols, changeState, docState, listIndex, blockIndex) { - const firstOp = changeState.nextOp, insert = firstOp[insertIdx] - const objActor = firstOp[objActorIdx], objCtr = firstOp[objCtrIdx] - const objectId = objActor === null ? '_root' : `${objCtr}@${docState.actorIds[objActor]}` - const idActorIndex = changeState.actorIndex, idActor = docState.actorIds[idActorIndex] - let foundListElem = false, elemVisible = false, propState = {}, docOp - ;({ docOp, blockIndex } = readNextDocOp(docState, blockIndex)) - let docOpsConsumed = (docOp === null ? 0 : 1) - let docOpOldSuccNum = (docOp === null ? 0 : docOp[succNumIdx]) - let changeOp = null, changeOps = [], changeCols = [], predSeen = [], lastChangeKey = null - changeState.objectIds.add(objectId) - - // Merge the two inputs: the sequence of ops in the doc, and the sequence of ops in the change. - // At each iteration, we either output the doc's op (possibly updated based on the change's ops) - // or output an op from the change. - while (true) { - // The array `changeOps` contains operations from the change(s) we're applying. When the array - // is empty, we load changes from the change. Typically we load only a single operation at a - // time, with two exceptions: 1. all operations that update the same key or list element in the - // same object are put into changeOps at the same time (this is needed so that we can update the - // succ columns of the document ops correctly); 2. a run of consecutive insertions is also - // placed into changeOps in one go. - // - // When we have processed all the ops in changeOps we try to see whether there are further - // operations that we can also process while we're at it. Those operations must be for the same - // object, they must be for a key or list element that appears later in the document, they must - // either all be insertions or all be non-insertions, and if insertions, they must be - // consecutive. If these conditions are satisfied, that means the operations can be processed in - // the same pass. If we encounter an operation that does not meet these conditions, we leave - // changeOps empty, and this function returns after having processed any remaining document ops. - // - // Any operations that could not be processed in a single pass remain in changeState; applyOps - // will seek to the appropriate position and then call mergeDocChangeOps again. - if (changeOps.length === 0) { - foundListElem = false - - let nextOp = changeState.nextOp - while (!changeState.done && nextOp[idActorIdx] === idActorIndex && nextOp[insertIdx] === insert && - nextOp[objActorIdx] === firstOp[objActorIdx] && nextOp[objCtrIdx] === firstOp[objCtrIdx]) { - - // Check if the operation's pred references a previous operation in changeOps - const lastOp = (changeOps.length > 0) ? changeOps[changeOps.length - 1] : null - let isOverwrite = false - for (let i = 0; i < nextOp[predNumIdx]; i++) { - for (let prevOp of changeOps) { - if (nextOp[predActorIdx][i] === prevOp[idActorIdx] && nextOp[predCtrIdx][i] === prevOp[idCtrIdx]) { - isOverwrite = true - } - } - } - - // If any of the following `if` statements is true, we add `nextOp` to `changeOps`. If they - // are all false, we break out of the loop and stop adding to `changeOps`. - if (nextOp === firstOp) { - // First change operation in a mergeDocChangeOps call is always used - } else if (insert && lastOp !== null && nextOp[keyStrIdx] === null && - nextOp[keyActorIdx] === lastOp[idActorIdx] && - nextOp[keyCtrIdx] === lastOp[idCtrIdx]) { - // Collect consecutive insertions - } else if (!insert && lastOp !== null && nextOp[keyStrIdx] !== null && - nextOp[keyStrIdx] === lastOp[keyStrIdx] && !isOverwrite) { - // Collect several updates to the same key - } else if (!insert && lastOp !== null && - nextOp[keyStrIdx] === null && lastOp[keyStrIdx] === null && - nextOp[keyActorIdx] === lastOp[keyActorIdx] && - nextOp[keyCtrIdx] === lastOp[keyCtrIdx] && !isOverwrite) { - // Collect several updates to the same list element - } else if (!insert && lastOp === null && nextOp[keyStrIdx] === null && - docOp && docOp[insertIdx] && docOp[keyStrIdx] === null && - docOp[idActorIdx] === nextOp[keyActorIdx] && - docOp[idCtrIdx] === nextOp[keyCtrIdx]) { - // When updating/deleting list elements, keep going if the next elemId in the change - // equals the next elemId in the doc (i.e. we're updating several consecutive elements) - } else if (!insert && lastOp === null && nextOp[keyStrIdx] !== null && - lastChangeKey !== null && lastChangeKey < nextOp[keyStrIdx]) { - // Allow a single mergeDocChangeOps call to process changes to several keys in the same - // object, provided that they appear in ascending order - } else break - - lastChangeKey = (nextOp !== null) ? nextOp[keyStrIdx] : null - changeOps.push(changeState.nextOp) - changeCols.push(changeState.columns) - predSeen.push(new Array(changeState.nextOp[predNumIdx])) - readNextChangeOp(docState, changeState) - nextOp = changeState.nextOp - } - } - - if (changeOps.length > 0) changeOp = changeOps[0] - const inCorrectObject = docOp && docOp[objActorIdx] === changeOp[objActorIdx] && docOp[objCtrIdx] === changeOp[objCtrIdx] - const keyMatches = docOp && docOp[keyStrIdx] !== null && docOp[keyStrIdx] === changeOp[keyStrIdx] - const listElemMatches = docOp && docOp[keyStrIdx] === null && changeOp[keyStrIdx] === null && - ((!docOp[insertIdx] && docOp[keyActorIdx] === changeOp[keyActorIdx] && docOp[keyCtrIdx] === changeOp[keyCtrIdx]) || - (docOp[insertIdx] && docOp[idActorIdx] === changeOp[keyActorIdx] && docOp[idCtrIdx] === changeOp[keyCtrIdx])) - - // We keep going until we run out of ops in the change, except that even when we run out, we - // keep going until we have processed all doc ops for the current key/list element. - if (changeOps.length === 0 && !(inCorrectObject && (keyMatches || listElemMatches))) break - - let takeDocOp = false, takeChangeOps = 0 - - // The change operations come first if we are inserting list elements (seekToOp already - // determines the correct insertion position), if there is no document operation, if the next - // document operation is for a different object, or if the change op's string key is - // lexicographically first (TODO check ordering of keys beyond the basic multilingual plane). - if (insert || !inCorrectObject || - (docOp[keyStrIdx] === null && changeOp[keyStrIdx] !== null) || - (docOp[keyStrIdx] !== null && changeOp[keyStrIdx] !== null && changeOp[keyStrIdx] < docOp[keyStrIdx])) { - // Take the operations from the change - takeChangeOps = changeOps.length - if (!inCorrectObject && !foundListElem && changeOp[keyStrIdx] === null && !changeOp[insertIdx]) { - // This can happen if we first update one list element, then another one earlier in the - // list. That is not allowed: list element updates must occur in ascending order. - throw new RangeError("could not find list element with ID: " + - `${changeOp[keyCtrIdx]}@${docState.actorIds[changeOp[keyActorIdx]]}`) - } - - } else if (keyMatches || listElemMatches || foundListElem) { - // The doc operation is for the same key or list element in the same object as the change - // ops, so we merge them. First, if any of the change ops' `pred` matches the opId of the - // document operation, we update the document operation's `succ` accordingly. - for (let opIndex = 0; opIndex < changeOps.length; opIndex++) { - const op = changeOps[opIndex] - for (let i = 0; i < op[predNumIdx]; i++) { - if (op[predActorIdx][i] === docOp[idActorIdx] && op[predCtrIdx][i] === docOp[idCtrIdx]) { - // Insert into the doc op's succ list such that the lists remains sorted - let j = 0 - while (j < docOp[succNumIdx] && (docOp[succCtrIdx][j] < op[idCtrIdx] || - docOp[succCtrIdx][j] === op[idCtrIdx] && docState.actorIds[docOp[succActorIdx][j]] < idActor)) j++ - docOp[succCtrIdx].splice(j, 0, op[idCtrIdx]) - docOp[succActorIdx].splice(j, 0, idActorIndex) - docOp[succNumIdx]++ - predSeen[opIndex][i] = true - break - } - } - } - - if (listElemMatches) foundListElem = true - - if (foundListElem && !listElemMatches) { - // If the previous docOp was for the correct list element, and the current docOp is for - // the wrong list element, then place the current changeOp before the docOp. - takeChangeOps = changeOps.length - - } else if (changeOps.length === 0 || docOp[idCtrIdx] < changeOp[idCtrIdx] || - (docOp[idCtrIdx] === changeOp[idCtrIdx] && docState.actorIds[docOp[idActorIdx]] < idActor)) { - // When we have several operations for the same object and the same key, we want to keep - // them sorted in ascending order by opId. Here we have docOp with a lower opId, so we - // output it first. - takeDocOp = true - updatePatchProperty(patches, newBlock, objectId, docOp, docState, propState, listIndex, docOpOldSuccNum) - - // A deletion op in the change is represented in the document only by its entries in the - // succ list of the operations it overwrites; it has no separate row in the set of ops. - for (let i = changeOps.length - 1; i >= 0; i--) { - let deleted = true - for (let j = 0; j < changeOps[i][predNumIdx]; j++) { - if (!predSeen[i][j]) deleted = false - } - if (ACTIONS[changeOps[i][actionIdx]] === 'del' && deleted) { - changeOps.splice(i, 1) - changeCols.splice(i, 1) - predSeen.splice(i, 1) - } - } - - } else if (docOp[idCtrIdx] === changeOp[idCtrIdx] && docState.actorIds[docOp[idActorIdx]] === idActor) { - throw new RangeError(`duplicate operation ID: ${changeOp[idCtrIdx]}@${idActor}`) - } else { - // The changeOp has the lower opId, so we output it first. - takeChangeOps = 1 - } - } else { - // The document operation comes first if its string key is lexicographically first, or if - // we're using opId keys and the keys don't match (i.e. we scan the document until we find a - // matching key). - takeDocOp = true - } - - if (takeDocOp) { - appendOperation(outCols, docState.blocks[blockIndex].columns, docOp) - addBlockOperation(newBlock, docOp, objectId, docState.actorIds, false) - - if (docOp[insertIdx] && elemVisible) { - elemVisible = false - listIndex++ - } - if (docOp[succNumIdx] === 0) elemVisible = true - newBlock.numOps++ - ;({ docOp, blockIndex } = readNextDocOp(docState, blockIndex)) - if (docOp !== null) { - docOpsConsumed++ - docOpOldSuccNum = docOp[succNumIdx] - } - } - - if (takeChangeOps > 0) { - for (let i = 0; i < takeChangeOps; i++) { - let op = changeOps[i] - // Check that we've seen all ops mentioned in `pred` (they must all have lower opIds than - // the change op's own opId, so we must have seen them already) - for (let j = 0; j < op[predNumIdx]; j++) { - if (!predSeen[i][j]) { - throw new RangeError(`no matching operation for pred: ${op[predCtrIdx][j]}@${docState.actorIds[op[predActorIdx][j]]}`) - } - } - updatePatchProperty(patches, newBlock, objectId, op, docState, propState, listIndex) - appendOperation(outCols, changeCols[i], op) - addBlockOperation(newBlock, op, objectId, docState.actorIds, true) - - if (op[insertIdx]) { - elemVisible = false - listIndex++ - } else { - elemVisible = true - } - } - - if (takeChangeOps === changeOps.length) { - changeOps.length = 0 - changeCols.length = 0 - predSeen.length = 0 - } else { - changeOps.splice(0, takeChangeOps) - changeCols.splice(0, takeChangeOps) - predSeen.splice(0, takeChangeOps) - } - newBlock.numOps += takeChangeOps - } - } - - if (docOp) { - appendOperation(outCols, docState.blocks[blockIndex].columns, docOp) - newBlock.numOps++ - if (docOp[objActorIdx] === objActor && docOp[objCtrIdx] === objCtr) { - addBlockOperation(newBlock, docOp, objectId, docState.actorIds, false) - } - } - return {docOpsConsumed, blockIndex} -} - -/** - * Applies operations from the change (or series of changes) in `changeState` to the document - * `docState`. Passing `changeState` to `readNextChangeOp` allows iterating over the change ops. - * `docState` is an object with keys: - * - `actorIds` is an array of actorIds (as hex strings) occurring in the document (values in - * the document's objActor/keyActor/idActor/... columns are indexes into this array). - * - `blocks` is an array of all the blocks of operations in the document. - * - `objectMeta` is a map from objectId to metadata about that object. - * - * `docState` is mutated to contain the updated document state. - * `patches` is a patch object that is mutated to reflect the operations applied by this function. - */ -function applyOps(patches, changeState, docState) { - const [objActorNum, objCtr, keyActorNum, keyCtr, keyStr, idActorNum, idCtr, insert] = changeState.nextOp - const objActor = objActorNum === null ? null : docState.actorIds[objActorNum] - const keyActor = keyActorNum === null ? null : docState.actorIds[keyActorNum] - const ops = { - objActor, objCtr, keyActor, keyCtr, keyStr, idActor: docState.actorIds[idActorNum], idCtr, insert, - objId: objActor === null ? '_root' : `${objCtr}@${objActor}` - } - - const {blockIndex, skipCount, visibleCount} = seekToOp(docState, ops) - const block = docState.blocks[blockIndex] - for (let col of block.columns) col.decoder.reset() - - const resetFirstVisible = (skipCount === 0) || (block.firstVisibleActor === undefined) || - (!insert && block.firstVisibleActor === keyActorNum && block.firstVisibleCtr === keyCtr) - const newBlock = { - columns: undefined, - bloom: new Uint8Array(block.bloom), - lastKey: copyObject(block.lastKey), - numVisible: copyObject(block.numVisible), - numOps: skipCount, - lastObjectActor: block.lastObjectActor, - lastObjectCtr: block.lastObjectCtr, - firstVisibleActor: resetFirstVisible ? undefined : block.firstVisibleActor, - firstVisibleCtr: resetFirstVisible ? undefined : block.firstVisibleCtr, - lastVisibleActor: undefined, - lastVisibleCtr: undefined - } - - // Copy the operations up to the insertion position (the first skipCount operations) - const outCols = block.columns.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) - copyColumns(outCols, block.columns, skipCount) - - // Apply the operations from the change. This may cause blockIndex to move forwards if the - // property being updated straddles a block boundary. - const {blockIndex: lastBlockIndex, docOpsConsumed} = - mergeDocChangeOps(patches, newBlock, outCols, changeState, docState, visibleCount, blockIndex) - - // Copy the remaining operations after the insertion position - const lastBlock = docState.blocks[lastBlockIndex] - let copyAfterMerge = -skipCount - docOpsConsumed - for (let i = blockIndex; i <= lastBlockIndex; i++) copyAfterMerge += docState.blocks[i].numOps - copyColumns(outCols, lastBlock.columns, copyAfterMerge) - newBlock.numOps += copyAfterMerge - - for (let col of lastBlock.columns) { - if (!col.decoder.done) throw new RangeError(`excess ops in column ${col.columnId}`) - } - - newBlock.columns = outCols.map(col => { - const decoder = decoderByColumnId(col.columnId, col.encoder.buffer) - return {columnId: col.columnId, decoder} - }) - - if (blockIndex === lastBlockIndex && newBlock.numOps <= MAX_BLOCK_SIZE) { - // The result is just one output block - if (copyAfterMerge > 0 && block.lastVisibleActor !== undefined && block.lastVisibleCtr !== undefined) { - // It's possible that none of the ops after the merge point are visible, in which case the - // lastVisible may not be strictly correct, because it may refer to an operation before the - // merge point rather than a list element inserted by the current change. However, this doesn't - // matter, because the only purpose for which we need it is to check whether one block ends with - // the same visible element as the next block starts with (to avoid double-counting its index); - // if the last list element of a block is invisible, the exact value of lastVisible doesn't - // matter since it will be different from the next block's firstVisible in any case. - newBlock.lastVisibleActor = block.lastVisibleActor - newBlock.lastVisibleCtr = block.lastVisibleCtr - } - - docState.blocks[blockIndex] = newBlock - - } else { - // Oversized output block must be split into smaller blocks - const newBlocks = splitBlock(newBlock, docState.actorIds) - docState.blocks.splice(blockIndex, lastBlockIndex - blockIndex + 1, ...newBlocks) - } -} - -/** - * Updates the columns in a document's operation blocks to contain all the columns in a change - * (including any column types we don't recognise, which have been generated by a future version - * of Automerge). - */ -function updateBlockColumns(docState, changeCols) { - // Check that the columns of a change appear at the index at which we expect them to be - if (changeCols[objActorIdx ].columnId !== CHANGE_COLUMNS[objActorIdx ].columnId || CHANGE_COLUMNS[objActorIdx ].columnName !== 'objActor' || - changeCols[objCtrIdx ].columnId !== CHANGE_COLUMNS[objCtrIdx ].columnId || CHANGE_COLUMNS[objCtrIdx ].columnName !== 'objCtr' || - changeCols[keyActorIdx ].columnId !== CHANGE_COLUMNS[keyActorIdx ].columnId || CHANGE_COLUMNS[keyActorIdx ].columnName !== 'keyActor' || - changeCols[keyCtrIdx ].columnId !== CHANGE_COLUMNS[keyCtrIdx ].columnId || CHANGE_COLUMNS[keyCtrIdx ].columnName !== 'keyCtr' || - changeCols[keyStrIdx ].columnId !== CHANGE_COLUMNS[keyStrIdx ].columnId || CHANGE_COLUMNS[keyStrIdx ].columnName !== 'keyStr' || - changeCols[idActorIdx ].columnId !== CHANGE_COLUMNS[idActorIdx ].columnId || CHANGE_COLUMNS[idActorIdx ].columnName !== 'idActor' || - changeCols[idCtrIdx ].columnId !== CHANGE_COLUMNS[idCtrIdx ].columnId || CHANGE_COLUMNS[idCtrIdx ].columnName !== 'idCtr' || - changeCols[insertIdx ].columnId !== CHANGE_COLUMNS[insertIdx ].columnId || CHANGE_COLUMNS[insertIdx ].columnName !== 'insert' || - changeCols[actionIdx ].columnId !== CHANGE_COLUMNS[actionIdx ].columnId || CHANGE_COLUMNS[actionIdx ].columnName !== 'action' || - changeCols[valLenIdx ].columnId !== CHANGE_COLUMNS[valLenIdx ].columnId || CHANGE_COLUMNS[valLenIdx ].columnName !== 'valLen' || - changeCols[valRawIdx ].columnId !== CHANGE_COLUMNS[valRawIdx ].columnId || CHANGE_COLUMNS[valRawIdx ].columnName !== 'valRaw' || - changeCols[predNumIdx ].columnId !== CHANGE_COLUMNS[predNumIdx ].columnId || CHANGE_COLUMNS[predNumIdx ].columnName !== 'predNum' || - changeCols[predActorIdx].columnId !== CHANGE_COLUMNS[predActorIdx].columnId || CHANGE_COLUMNS[predActorIdx].columnName !== 'predActor' || - changeCols[predCtrIdx ].columnId !== CHANGE_COLUMNS[predCtrIdx ].columnId || CHANGE_COLUMNS[predCtrIdx ].columnName !== 'predCtr') { - throw new RangeError('unexpected columnId') - } - - // Check if there any columns in the change that are not in the document, apart from pred* - const docCols = docState.blocks[0].columns - if (!changeCols.every(changeCol => PRED_COLUMN_IDS.includes(changeCol.columnId) || - docCols.find(docCol => docCol.columnId === changeCol.columnId))) { - let allCols = docCols.map(docCol => ({columnId: docCol.columnId})) - for (let changeCol of changeCols) { - const { columnId } = changeCol - if (!PRED_COLUMN_IDS.includes(columnId) && !docCols.find(docCol => docCol.columnId === columnId)) { - allCols.push({columnId}) - } - } - allCols.sort((a, b) => a.columnId - b.columnId) - - for (let blockIndex = 0; blockIndex < docState.blocks.length; blockIndex++) { - let block = copyObject(docState.blocks[blockIndex]) - block.columns = makeDecoders(block.columns.map(col => ({columnId: col.columnId, buffer: col.decoder.buf})), allCols) - docState.blocks[blockIndex] = block - } - } -} - -/** - * Takes a decoded change header, including an array of actorIds. Returns an object of the form - * `{actorIds, actorTable}`, where `actorIds` is an updated array of actorIds appearing in the - * document (including the new change's actorId). `actorTable` is an array of integers where - * `actorTable[i]` contains the document's actor index for the actor that has index `i` in the - * change (`i == 0` is the author of the change). - */ -function getActorTable(actorIds, change) { - if (actorIds.indexOf(change.actorIds[0]) < 0) { - if (change.seq !== 1) { - throw new RangeError(`Seq ${change.seq} is the first change for actor ${change.actorIds[0]}`) - } - // Use concat, not push, so that the original array is not mutated - actorIds = actorIds.concat([change.actorIds[0]]) - } - const actorTable = [] // translate from change's actor index to doc's actor index - for (let actorId of change.actorIds) { - const index = actorIds.indexOf(actorId) - if (index < 0) { - throw new RangeError(`actorId ${actorId} is not known to document`) - } - actorTable.push(index) - } - return {actorIds, actorTable} -} - -/** - * Finalises the patch for a change. `patches` is a map from objectIds to patch for that - * particular object, `objectIds` is the array of IDs of objects that are created or updated in the - * change, and `docState` is an object containing various bits of document state, including - * `objectMeta`, a map from objectIds to metadata about that object (such as its parent in the - * document tree). Mutates `patches` such that child objects are linked into their parent object, - * all the way to the root object. - */ -function setupPatches(patches, objectIds, docState) { - for (let objectId of objectIds) { - let meta = docState.objectMeta[objectId], childMeta = null, patchExists = false - while (true) { - const hasChildren = childMeta && Object.keys(meta.children[childMeta.parentKey]).length > 0 - if (!patches[objectId]) patches[objectId] = emptyObjectPatch(objectId, meta.type) - - if (childMeta && hasChildren) { - if (meta.type === 'list' || meta.type === 'text') { - // In list/text objects, parentKey is an elemID. First see if it already appears in an edit - for (let edit of patches[objectId].edits) { - if (edit.opId && meta.children[childMeta.parentKey][edit.opId]) { - patchExists = true - } - } - - // If we need to add an edit, we first have to translate the elemId into an index - if (!patchExists) { - const obj = parseOpId(objectId), elem = parseOpId(childMeta.parentKey) - const seekPos = { - objActor: obj.actorId, objCtr: obj.counter, - keyActor: elem.actorId, keyCtr: elem.counter, - keyStr: null, insert: false, - objId: objectId - } - const { visibleCount } = seekToOp(docState, seekPos) - - for (let [opId, value] of Object.entries(meta.children[childMeta.parentKey])) { - let patchValue = value - if (value.objectId) { - if (!patches[value.objectId]) patches[value.objectId] = emptyObjectPatch(value.objectId, value.type) - patchValue = patches[value.objectId] - } - const edit = {action: 'update', index: visibleCount, opId, value: patchValue} - appendEdit(patches[objectId].edits, edit) - } - } - - } else { - // Non-list object: parentKey is the name of the property being updated (a string) - if (!patches[objectId].props[childMeta.parentKey]) { - patches[objectId].props[childMeta.parentKey] = {} - } - let values = patches[objectId].props[childMeta.parentKey] - - for (let [opId, value] of Object.entries(meta.children[childMeta.parentKey])) { - if (values[opId]) { - patchExists = true - } else if (value.objectId) { - if (!patches[value.objectId]) patches[value.objectId] = emptyObjectPatch(value.objectId, value.type) - values[opId] = patches[value.objectId] - } else { - values[opId] = value - } - } - } - } - - if (patchExists || !meta.parentObj || (childMeta && !hasChildren)) break - childMeta = meta - objectId = meta.parentObj - meta = docState.objectMeta[objectId] - } - } - return patches -} - -/** - * Takes an array of decoded changes and applies them to a document. `docState` contains a bunch of - * fields describing the document state. This function mutates `docState` to contain the updated - * document state, and mutates `patches` to contain a patch to return to the frontend. Only the - * top-level `docState` object is mutated; all nested objects within it are treated as immutable. - * `objectIds` is mutated to contain the IDs of objects that are updated in any of the changes. - * - * Returns a two-element array `[applied, enqueued]`, where `applied` is an array of changes that - * have been applied to the document, and `enqueued` is an array of changes that have not yet been - * applied because they are missing a dependency. - */ -function applyChanges(patches, decodedChanges, docState, objectIds) { - let heads = new Set(docState.heads), changeHashes = new Set() - let clock = copyObject(docState.clock) - let applied = [], enqueued = [] - - for (let change of decodedChanges) { - // Skip any duplicate changes that we have already seen - if (docState.changeIndexByHash[change.hash] !== undefined || changeHashes.has(change.hash)) continue - - let causallyReady = true - for (let dep of change.deps) { - const depIndex = docState.changeIndexByHash[dep] - if ((depIndex === undefined || depIndex === -1) && !changeHashes.has(dep)) { - causallyReady = false - } - } - - if (causallyReady) { - const expectedSeq = (clock[change.actor] || 0) + 1 - if (change.seq !== expectedSeq) { - throw new RangeError(`Expected seq ${expectedSeq}, got seq ${change.seq} from actor ${change.actor}`) - } - clock[change.actor] = change.seq - changeHashes.add(change.hash) - for (let dep of change.deps) heads.delete(dep) - heads.add(change.hash) - applied.push(change) - } else { - enqueued.push(change) - } - } - - if (applied.length > 0) { - let changeState = {changes: applied, changeIndex: -1, objectIds} - readNextChangeOp(docState, changeState) - while (!changeState.done) applyOps(patches, changeState, docState) - - docState.heads = [...heads].sort() - docState.clock = clock - } - return [applied, enqueued] -} - -/** - * Scans the operations in a document and generates a patch that can be sent to the frontend to - * instantiate the current state of the document. `objectMeta` is mutated to contain information - * about the parent and children of each object in the document. - */ -function documentPatch(docState) { - for (let col of docState.blocks[0].columns) col.decoder.reset() - let propState = {}, docOp = null, blockIndex = 0 - let patches = {_root: {objectId: '_root', type: 'map', props: {}}} - let lastObjActor = null, lastObjCtr = null, objectId = '_root', elemVisible = false, listIndex = 0 - - while (true) { - ({ docOp, blockIndex } = readNextDocOp(docState, blockIndex)) - if (docOp === null) break - if (docOp[objActorIdx] !== lastObjActor || docOp[objCtrIdx] !== lastObjCtr) { - objectId = `${docOp[objCtrIdx]}@${docState.actorIds[docOp[objActorIdx]]}` - lastObjActor = docOp[objActorIdx] - lastObjCtr = docOp[objCtrIdx] - propState = {} - listIndex = 0 - elemVisible = false - } - - if (docOp[insertIdx] && elemVisible) { - elemVisible = false - listIndex++ - } - if (docOp[succNumIdx] === 0) elemVisible = true - if (docOp[idCtrIdx] > docState.maxOp) docState.maxOp = docOp[idCtrIdx] - for (let i = 0; i < docOp[succNumIdx]; i++) { - if (docOp[succCtrIdx][i] > docState.maxOp) docState.maxOp = docOp[succCtrIdx][i] - } - - updatePatchProperty(patches, null, objectId, docOp, docState, propState, listIndex, docOp[succNumIdx]) - } - return patches._root -} - -/** - * Takes an encoded document whose headers have been parsed using `decodeDocumentHeader()` and reads - * from it the list of changes. Returns the document's current vector clock, i.e. an object mapping - * each actor ID (as a hex string) to the number of changes seen from that actor. Also returns an - * array of the actorIds whose most recent change has no dependents (i.e. the actors that - * contributed the current heads of the document), and an array of encoders that has been - * initialised to contain the columns of the changes list. - */ -function readDocumentChanges(doc) { - const columns = makeDecoders(doc.changesColumns, DOCUMENT_COLUMNS) - const actorD = columns[0].decoder, seqD = columns[1].decoder - const depsNumD = columns[5].decoder, depsIndexD = columns[6].decoder - if (columns[0].columnId !== DOCUMENT_COLUMNS[0].columnId || DOCUMENT_COLUMNS[0].columnName !== 'actor' || - columns[1].columnId !== DOCUMENT_COLUMNS[1].columnId || DOCUMENT_COLUMNS[1].columnName !== 'seq' || - columns[5].columnId !== DOCUMENT_COLUMNS[5].columnId || DOCUMENT_COLUMNS[5].columnName !== 'depsNum' || - columns[6].columnId !== DOCUMENT_COLUMNS[6].columnId || DOCUMENT_COLUMNS[6].columnName !== 'depsIndex') { - throw new RangeError('unexpected columnId') - } - - let numChanges = 0, clock = {}, actorNums = [], headIndexes = new Set() - while (!actorD.done) { - const actorNum = actorD.readValue(), seq = seqD.readValue(), depsNum = depsNumD.readValue() - const actorId = doc.actorIds[actorNum] - if (seq !== 1 && seq !== clock[actorId] + 1) { - throw new RangeError(`Expected seq ${clock[actorId] + 1}, got ${seq} for actor ${actorId}`) - } - actorNums.push(actorNum) - clock[actorId] = seq - headIndexes.add(numChanges) - for (let j = 0; j < depsNum; j++) headIndexes.delete(depsIndexD.readValue()) - numChanges++ - } - const headActors = [...headIndexes].map(index => doc.actorIds[actorNums[index]]).sort() - - for (let col of columns) col.decoder.reset() - const encoders = columns.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) - copyColumns(encoders, columns, numChanges) - return {clock, headActors, encoders, numChanges} -} - -/** - * Records the metadata about a change in the appropriate columns. - */ -function appendChange(columns, change, actorIds, changeIndexByHash) { - appendOperation(columns, DOCUMENT_COLUMNS, [ - actorIds.indexOf(change.actor), // actor - change.seq, // seq - change.maxOp, // maxOp - change.time, // time - change.message, // message - change.deps.length, // depsNum - change.deps.map(dep => changeIndexByHash[dep]), // depsIndex - change.extraBytes ? (change.extraBytes.byteLength << 4 | VALUE_TYPE.BYTES) : VALUE_TYPE.BYTES, // extraLen - change.extraBytes // extraRaw - ]) -} - -class BackendDoc { - constructor(buffer) { - this.maxOp = 0 - this.haveHashGraph = false - this.changes = [] - this.changeIndexByHash = {} - this.dependenciesByHash = {} - this.dependentsByHash = {} - this.hashesByActor = {} - this.actorIds = [] - this.heads = [] - this.clock = {} - this.queue = [] - this.objectMeta = {_root: {parentObj: null, parentKey: null, opId: null, type: 'map', children: {}}} - - if (buffer) { - const doc = decodeDocumentHeader(buffer) - const {clock, headActors, encoders, numChanges} = readDocumentChanges(doc) - this.binaryDoc = buffer - this.changes = new Array(numChanges) - this.actorIds = doc.actorIds - this.heads = doc.heads - this.clock = clock - this.changesEncoders = encoders - this.extraBytes = doc.extraBytes - - // If there is a single head, we can unambiguously point at the actorId and sequence number of - // the head hash without having to reconstruct the hash graph - if (doc.heads.length === 1 && headActors.length === 1) { - this.hashesByActor[headActors[0]] = [] - this.hashesByActor[headActors[0]][clock[headActors[0]] - 1] = doc.heads[0] - } - - // The encoded document gives each change an index, and expresses dependencies in terms of - // those indexes. Initialise the translation table from hash to index. - if (doc.heads.length === doc.headsIndexes.length) { - for (let i = 0; i < doc.heads.length; i++) { - this.changeIndexByHash[doc.heads[i]] = doc.headsIndexes[i] - } - } else if (doc.heads.length === 1) { - // If there is only one head, it must be the last change - this.changeIndexByHash[doc.heads[0]] = numChanges - 1 - } else { - // We know the heads hashes, but not their indexes - for (let head of doc.heads) this.changeIndexByHash[head] = -1 - } - - this.blocks = [{columns: makeDecoders(doc.opsColumns, DOC_OPS_COLUMNS)}] - updateBlockMetadata(this.blocks[0], this.actorIds) - if (this.blocks[0].numOps > MAX_BLOCK_SIZE) { - this.blocks = splitBlock(this.blocks[0], this.actorIds) - } - - let docState = {blocks: this.blocks, actorIds: this.actorIds, objectMeta: this.objectMeta, maxOp: 0} - this.initPatch = documentPatch(docState) - this.maxOp = docState.maxOp - - } else { - this.haveHashGraph = true - this.changesEncoders = DOCUMENT_COLUMNS.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) - this.blocks = [{ - columns: makeDecoders([], DOC_OPS_COLUMNS), - bloom: new Uint8Array(BLOOM_FILTER_SIZE), - lastKey: {}, - numVisible: {}, - numOps: 0, - lastObjectActor: undefined, - lastObjectCtr: undefined, - firstVisibleActor: undefined, - firstVisibleCtr: undefined, - lastVisibleActor: undefined, - lastVisibleCtr: undefined - }] - } - } - - /** - * Makes a copy of this BackendDoc that can be independently modified. - */ - clone() { - let copy = new BackendDoc() - copy.maxOp = this.maxOp - copy.haveHashGraph = this.haveHashGraph - copy.changes = this.changes.slice() - copy.changeIndexByHash = copyObject(this.changeIndexByHash) - copy.dependenciesByHash = copyObject(this.dependenciesByHash) - copy.dependentsByHash = Object.entries(this.dependentsByHash).reduce((acc, [k, v]) => { acc[k] = v.slice(); return acc }, {}) - copy.hashesByActor = Object.entries(this.hashesByActor).reduce((acc, [k, v]) => { acc[k] = v.slice(); return acc }, {}) - copy.actorIds = this.actorIds // immutable, no copying needed - copy.heads = this.heads // immutable, no copying needed - copy.clock = this.clock // immutable, no copying needed - copy.blocks = this.blocks // immutable, no copying needed - copy.objectMeta = this.objectMeta // immutable, no copying needed - copy.queue = this.queue // immutable, no copying needed - return copy - } - - /** - * Parses the changes given as Uint8Arrays in `changeBuffers`, and applies them to the current - * document. Returns a patch to apply to the frontend. If an exception is thrown, the document - * object is not modified. - */ - applyChanges(changeBuffers, isLocal = false) { - // decoded change has the form { actor, seq, startOp, time, message, deps, actorIds, hash, columns, buffer } - let decodedChanges = changeBuffers.map(buffer => { - const decoded = decodeChangeColumns(buffer) - decoded.buffer = buffer - return decoded - }) - - let patches = {_root: {objectId: '_root', type: 'map', props: {}}} - let docState = { - maxOp: this.maxOp, - changeIndexByHash: this.changeIndexByHash, - actorIds: this.actorIds, - heads: this.heads, - clock: this.clock, - blocks: this.blocks.slice(), - objectMeta: Object.assign({}, this.objectMeta) - } - let queue = (this.queue.length === 0) ? decodedChanges : decodedChanges.concat(this.queue) - let allApplied = [], objectIds = new Set() - - while (true) { - const [applied, enqueued] = applyChanges(patches, queue, docState, objectIds) - queue = enqueued - if (applied.length > 0) allApplied = allApplied.concat(applied) - if (queue.length === 0) break - - // If we are missing a dependency, and we haven't computed the hash graph yet, first compute - // the hashes to see if we actually have it already - if (applied.length === 0) { - if (!this.haveHashGraph) this.computeHashGraph(); else break - } - } - - setupPatches(patches, objectIds, docState) - - // Update the document state only if `applyChanges` does not throw an exception - for (let change of allApplied) { - this.changes.push(change.buffer) - if (!this.hashesByActor[change.actor]) this.hashesByActor[change.actor] = [] - this.hashesByActor[change.actor][change.seq - 1] = change.hash - this.changeIndexByHash[change.hash] = this.changes.length - 1 - this.dependenciesByHash[change.hash] = change.deps - this.dependentsByHash[change.hash] = [] - for (let dep of change.deps) { - if (!this.dependentsByHash[dep]) this.dependentsByHash[dep] = [] - this.dependentsByHash[dep].push(change.hash) - } - appendChange(this.changesEncoders, change, docState.actorIds, this.changeIndexByHash) - } - - this.maxOp = docState.maxOp - this.actorIds = docState.actorIds - this.heads = docState.heads - this.clock = docState.clock - this.blocks = docState.blocks - this.objectMeta = docState.objectMeta - this.queue = queue - this.binaryDoc = null - this.initPatch = null - - let patch = { - maxOp: this.maxOp, clock: this.clock, deps: this.heads, - pendingChanges: this.queue.length, diffs: patches._root - } - if (isLocal && decodedChanges.length === 1) { - patch.actor = decodedChanges[0].actor - patch.seq = decodedChanges[0].seq - } - return patch - } - - /** - * Reconstructs the full change history of a document, and initialises the variables that allow us - * to traverse the hash graph of changes and their dependencies. When a compressed document is - * loaded we defer the computation of this hash graph to make loading faster, but if the hash - * graph is later needed (e.g. for the sync protocol), this function fills it in. - */ - computeHashGraph() { - const binaryDoc = this.save() - this.haveHashGraph = true - this.changes = [] - this.changeIndexByHash = {} - this.dependenciesByHash = {} - this.dependentsByHash = {} - this.hashesByActor = {} - this.clock = {} - - for (let change of decodeChanges([binaryDoc])) { - const binaryChange = encodeChange(change) // TODO: avoid decoding and re-encoding again - this.changes.push(binaryChange) - this.changeIndexByHash[change.hash] = this.changes.length - 1 - this.dependenciesByHash[change.hash] = change.deps - this.dependentsByHash[change.hash] = [] - for (let dep of change.deps) this.dependentsByHash[dep].push(change.hash) - if (change.seq === 1) this.hashesByActor[change.actor] = [] - this.hashesByActor[change.actor].push(change.hash) - const expectedSeq = (this.clock[change.actor] || 0) + 1 - if (change.seq !== expectedSeq) { - throw new RangeError(`Expected seq ${expectedSeq}, got seq ${change.seq} from actor ${change.actor}`) - } - this.clock[change.actor] = change.seq - } - } - - /** - * Returns all the changes that need to be sent to another replica. `haveDeps` is a list of change - * hashes (as hex strings) of the heads that the other replica has. The changes in `haveDeps` and - * any of their transitive dependencies will not be returned; any changes later than or concurrent - * to the hashes in `haveDeps` will be returned. If `haveDeps` is an empty array, all changes are - * returned. Throws an exception if any of the given hashes are not known to this replica. - */ - getChanges(haveDeps) { - if (!this.haveHashGraph) this.computeHashGraph() - - // If the other replica has nothing, return all changes in history order - if (haveDeps.length === 0) { - return this.changes.slice() - } - - // Fast path for the common case where all new changes depend only on haveDeps - let stack = [], seenHashes = {}, toReturn = [] - for (let hash of haveDeps) { - seenHashes[hash] = true - const successors = this.dependentsByHash[hash] - if (!successors) throw new RangeError(`hash not found: ${hash}`) - stack.push(...successors) - } - - // Depth-first traversal of the hash graph to find all changes that depend on `haveDeps` - while (stack.length > 0) { - const hash = stack.pop() - seenHashes[hash] = true - toReturn.push(hash) - if (!this.dependenciesByHash[hash].every(dep => seenHashes[dep])) { - // If a change depends on a hash we have not seen, abort the traversal and fall back to the - // slower algorithm. This will sometimes abort even if all new changes depend on `haveDeps`, - // because our depth-first traversal is not necessarily a topological sort of the graph. - break - } - stack.push(...this.dependentsByHash[hash]) - } - - // If the traversal above has encountered all the heads, and was not aborted early due to - // a missing dependency, then the set of changes it has found is complete, so we can return it - if (stack.length === 0 && this.heads.every(head => seenHashes[head])) { - return toReturn.map(hash => this.changes[this.changeIndexByHash[hash]]) - } - - // If we haven't encountered all of the heads, we have to search harder. This will happen if - // changes were added that are concurrent to `haveDeps` - stack = haveDeps.slice() - seenHashes = {} - while (stack.length > 0) { - const hash = stack.pop() - if (!seenHashes[hash]) { - const deps = this.dependenciesByHash[hash] - if (!deps) throw new RangeError(`hash not found: ${hash}`) - stack.push(...deps) - seenHashes[hash] = true - } - } - - return this.changes.filter(change => !seenHashes[decodeChangeMeta(change, true).hash]) - } - - /** - * Returns all changes that are present in this BackendDoc, but not present in the `other` - * BackendDoc. - */ - getChangesAdded(other) { - if (!this.haveHashGraph) this.computeHashGraph() - - // Depth-first traversal from the heads through the dependency graph, - // until we reach a change that is already present in opSet1 - let stack = this.heads.slice(), seenHashes = {}, toReturn = [] - while (stack.length > 0) { - const hash = stack.pop() - if (!seenHashes[hash] && other.changeIndexByHash[hash] === undefined) { - seenHashes[hash] = true - toReturn.push(hash) - stack.push(...this.dependenciesByHash[hash]) - } - } - - // Return those changes in the reverse of the order in which the depth-first search - // found them. This is not necessarily a topological sort, but should usually be close. - return toReturn.reverse().map(hash => this.changes[this.changeIndexByHash[hash]]) - } - - getChangeByHash(hash) { - if (!this.haveHashGraph) this.computeHashGraph() - return this.changes[this.changeIndexByHash[hash]] - } - - /** - * Returns the hashes of any missing dependencies, i.e. where we have tried to apply a change that - * has a dependency on a change we have not seen. - * - * If the argument `heads` is given (an array of hexadecimal strings representing hashes as - * returned by `getHeads()`), this function also ensures that all of those hashes resolve to - * either a change that has been applied to the document, or that has been enqueued for later - * application once missing dependencies have arrived. Any missing heads hashes are included in - * the returned array. - */ - getMissingDeps(heads = []) { - if (!this.haveHashGraph) this.computeHashGraph() - - let allDeps = new Set(heads), inQueue = new Set() - for (let change of this.queue) { - inQueue.add(change.hash) - for (let dep of change.deps) allDeps.add(dep) - } - - let missing = [] - for (let hash of allDeps) { - if (this.changeIndexByHash[hash] === undefined && !inQueue.has(hash)) missing.push(hash) - } - return missing.sort() - } - - /** - * Serialises the current document state into a single byte array. - */ - save() { - if (this.binaryDoc) return this.binaryDoc - - // Getting the byte array for the changes columns finalises their encoders, after which we can - // no longer append values to them. We therefore copy their data over to fresh encoders. - const newEncoders = this.changesEncoders.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) - const decoders = this.changesEncoders.map(col => { - const decoder = decoderByColumnId(col.columnId, col.encoder.buffer) - return {columnId: col.columnId, decoder} - }) - copyColumns(newEncoders, decoders, this.changes.length) - - this.binaryDoc = encodeDocumentHeader({ - changesColumns: this.changesEncoders, - opsColumns: concatBlocks(this.blocks), - actorIds: this.actorIds, // TODO: sort actorIds (requires transforming all actorId columns in opsColumns) - heads: this.heads, - headsIndexes: this.heads.map(hash => this.changeIndexByHash[hash]), - extraBytes: this.extraBytes - }) - this.changesEncoders = newEncoders - return this.binaryDoc - } - - /** - * Returns a patch from which we can initialise the current state of the backend. - */ - getPatch() { - const objectMeta = {_root: {parentObj: null, parentKey: null, opId: null, type: 'map', children: {}}} - const docState = {blocks: this.blocks, actorIds: this.actorIds, objectMeta, maxOp: 0} - const diffs = this.initPatch ? this.initPatch : documentPatch(docState) - return { - maxOp: this.maxOp, clock: this.clock, deps: this.heads, - pendingChanges: this.queue.length, diffs - } - } -} - -module.exports = { MAX_BLOCK_SIZE, BackendDoc, bloomFilterContains } - -},{"../src/common":85,"./columnar":47}],51:[function(require,module,exports){ -/** - * Implementation of the data synchronisation protocol that brings a local and a remote document - * into the same state. This is typically used when two nodes have been disconnected for some time, - * and need to exchange any changes that happened while they were disconnected. The two nodes that - * are syncing could be client and server, or server and client, or two peers with symmetric roles. - * - * The protocol is based on this paper: Martin Kleppmann and Heidi Howard. Byzantine Eventual - * Consistency and the Fundamental Limits of Peer-to-Peer Databases. https://arxiv.org/abs/2012.00472 - * - * The protocol assumes that every time a node successfully syncs with another node, it remembers - * the current heads (as returned by `Backend.getHeads()`) after the last sync with that node. The - * next time we try to sync with the same node, we start from the assumption that the other node's - * document version is no older than the outcome of the last sync, so we only need to exchange any - * changes that are more recent than the last sync. This assumption may not be true if the other - * node did not correctly persist its state (perhaps it crashed before writing the result of the - * last sync to disk), and we fall back to sending the entire document in this case. - */ - -const Backend = require('./backend') -const { hexStringToBytes, bytesToHexString, Encoder, Decoder } = require('./encoding') -const { decodeChangeMeta } = require('./columnar') -const { copyObject } = require('../src/common') - -const HASH_SIZE = 32 // 256 bits = 32 bytes -const MESSAGE_TYPE_SYNC = 0x42 // first byte of a sync message, for identification -const PEER_STATE_TYPE = 0x43 // first byte of an encoded peer state, for identification - -// These constants correspond to a 1% false positive rate. The values can be changed without -// breaking compatibility of the network protocol, since the parameters used for a particular -// Bloom filter are encoded in the wire format. -const BITS_PER_ENTRY = 10, NUM_PROBES = 7 - -/** - * A Bloom filter implementation that can be serialised to a byte array for transmission - * over a network. The entries that are added are assumed to already be SHA-256 hashes, - * so this implementation does not perform its own hashing. - */ -class BloomFilter { - constructor (arg) { - if (Array.isArray(arg)) { - // arg is an array of SHA256 hashes in hexadecimal encoding - this.numEntries = arg.length - this.numBitsPerEntry = BITS_PER_ENTRY - this.numProbes = NUM_PROBES - this.bits = new Uint8Array(Math.ceil(this.numEntries * this.numBitsPerEntry / 8)) - for (let hash of arg) this.addHash(hash) - } else if (arg instanceof Uint8Array) { - if (arg.byteLength === 0) { - this.numEntries = 0 - this.numBitsPerEntry = 0 - this.numProbes = 0 - this.bits = arg - } else { - const decoder = new Decoder(arg) - this.numEntries = decoder.readUint32() - this.numBitsPerEntry = decoder.readUint32() - this.numProbes = decoder.readUint32() - this.bits = decoder.readRawBytes(Math.ceil(this.numEntries * this.numBitsPerEntry / 8)) - } - } else { - throw new TypeError('invalid argument') - } - } - - /** - * Returns the Bloom filter state, encoded as a byte array. - */ - get bytes() { - if (this.numEntries === 0) return new Uint8Array(0) - const encoder = new Encoder() - encoder.appendUint32(this.numEntries) - encoder.appendUint32(this.numBitsPerEntry) - encoder.appendUint32(this.numProbes) - encoder.appendRawBytes(this.bits) - return encoder.buffer - } - - /** - * Given a SHA-256 hash (as hex string), returns an array of probe indexes indicating which bits - * in the Bloom filter need to be tested or set for this particular entry. We do this by - * interpreting the first 12 bytes of the hash as three little-endian 32-bit unsigned integers, - * and then using triple hashing to compute the probe indexes. The algorithm comes from: - * - * Peter C. Dillinger and Panagiotis Manolios. Bloom Filters in Probabilistic Verification. - * 5th International Conference on Formal Methods in Computer-Aided Design (FMCAD), November 2004. - * http://www.ccis.northeastern.edu/home/pete/pub/bloom-filters-verification.pdf - */ - getProbes(hash) { - const hashBytes = hexStringToBytes(hash), modulo = 8 * this.bits.byteLength - if (hashBytes.byteLength !== 32) throw new RangeError(`Not a 256-bit hash: ${hash}`) - // on the next three lines, the right shift means interpret value as unsigned - let x = ((hashBytes[0] | hashBytes[1] << 8 | hashBytes[2] << 16 | hashBytes[3] << 24) >>> 0) % modulo - let y = ((hashBytes[4] | hashBytes[5] << 8 | hashBytes[6] << 16 | hashBytes[7] << 24) >>> 0) % modulo - let z = ((hashBytes[8] | hashBytes[9] << 8 | hashBytes[10] << 16 | hashBytes[11] << 24) >>> 0) % modulo - const probes = [x] - for (let i = 1; i < this.numProbes; i++) { - x = (x + y) % modulo - y = (y + z) % modulo - probes.push(x) - } - return probes - } - - /** - * Sets the Bloom filter bits corresponding to a given SHA-256 hash (given as hex string). - */ - addHash(hash) { - for (let probe of this.getProbes(hash)) { - this.bits[probe >>> 3] |= 1 << (probe & 7) - } - } - - /** - * Tests whether a given SHA-256 hash (given as hex string) is contained in the Bloom filter. - */ - containsHash(hash) { - if (this.numEntries === 0) return false - for (let probe of this.getProbes(hash)) { - if ((this.bits[probe >>> 3] & (1 << (probe & 7))) === 0) { - return false - } - } - return true - } -} - -/** - * Encodes a sorted array of SHA-256 hashes (as hexadecimal strings) into a byte array. - */ -function encodeHashes(encoder, hashes) { - if (!Array.isArray(hashes)) throw new TypeError('hashes must be an array') - encoder.appendUint32(hashes.length) - for (let i = 0; i < hashes.length; i++) { - if (i > 0 && hashes[i - 1] >= hashes[i]) throw new RangeError('hashes must be sorted') - const bytes = hexStringToBytes(hashes[i]) - if (bytes.byteLength !== HASH_SIZE) throw new TypeError('heads hashes must be 256 bits') - encoder.appendRawBytes(bytes) - } -} - -/** - * Decodes a byte array in the format returned by encodeHashes(), and returns its content as an - * array of hex strings. - */ -function decodeHashes(decoder) { - let length = decoder.readUint32(), hashes = [] - for (let i = 0; i < length; i++) { - hashes.push(bytesToHexString(decoder.readRawBytes(HASH_SIZE))) - } - return hashes -} - -/** - * Takes a sync message of the form `{heads, need, have, changes}` and encodes it as a byte array for - * transmission. - */ -function encodeSyncMessage(message) { - const encoder = new Encoder() - encoder.appendByte(MESSAGE_TYPE_SYNC) - encodeHashes(encoder, message.heads) - encodeHashes(encoder, message.need) - encoder.appendUint32(message.have.length) - for (let have of message.have) { - encodeHashes(encoder, have.lastSync) - encoder.appendPrefixedBytes(have.bloom) - } - encoder.appendUint32(message.changes.length) - for (let change of message.changes) { - encoder.appendPrefixedBytes(change) - } - return encoder.buffer -} - -/** - * Takes a binary-encoded sync message and decodes it into the form `{heads, need, have, changes}`. - */ -function decodeSyncMessage(bytes) { - const decoder = new Decoder(bytes) - const messageType = decoder.readByte() - if (messageType !== MESSAGE_TYPE_SYNC) { - throw new RangeError(`Unexpected message type: ${messageType}`) - } - const heads = decodeHashes(decoder) - const need = decodeHashes(decoder) - const haveCount = decoder.readUint32() - let message = {heads, need, have: [], changes: []} - for (let i = 0; i < haveCount; i++) { - const lastSync = decodeHashes(decoder) - const bloom = decoder.readPrefixedBytes(decoder) - message.have.push({lastSync, bloom}) - } - const changeCount = decoder.readUint32() - for (let i = 0; i < changeCount; i++) { - const change = decoder.readPrefixedBytes() - message.changes.push(change) - } - // Ignore any trailing bytes -- they can be used for extensions by future versions of the protocol - return message -} - -/** - * Takes a SyncState and encodes as a byte array those parts of the state that should persist across - * an application restart or disconnect and reconnect. The ephemeral parts of the state that should - * be cleared on reconnect are not encoded. - */ -function encodeSyncState(syncState) { - const encoder = new Encoder() - encoder.appendByte(PEER_STATE_TYPE) - encodeHashes(encoder, syncState.sharedHeads) - return encoder.buffer -} - -/** - * Takes a persisted peer state as encoded by `encodeSyncState` and decodes it into a SyncState - * object. The parts of the peer state that were not encoded are initialised with default values. - */ -function decodeSyncState(bytes) { - const decoder = new Decoder(bytes) - const recordType = decoder.readByte() - if (recordType !== PEER_STATE_TYPE) { - throw new RangeError(`Unexpected record type: ${recordType}`) - } - const sharedHeads = decodeHashes(decoder) - return Object.assign(initSyncState(), { sharedHeads }) -} - -/** - * Constructs a Bloom filter containing all changes that are not one of the hashes in - * `lastSync` or its transitive dependencies. In other words, the filter contains those - * changes that have been applied since the version identified by `lastSync`. Returns - * an object of the form `{lastSync, bloom}` as required for the `have` field of a sync - * message. - */ -function makeBloomFilter(backend, lastSync) { - const newChanges = Backend.getChanges(backend, lastSync) - const hashes = newChanges.map(change => decodeChangeMeta(change, true).hash) - return {lastSync, bloom: new BloomFilter(hashes).bytes} -} - -/** - * Call this function when a sync message is received from another node. The `message` argument - * needs to already have been decoded using `decodeSyncMessage()`. This function determines the - * changes that we need to send to the other node in response. Returns an array of changes (as - * byte arrays). - */ -function getChangesToSend(backend, have, need) { - if (have.length === 0) { - return need.map(hash => Backend.getChangeByHash(backend, hash)).filter(change => change !== undefined) - } - - let lastSyncHashes = {}, bloomFilters = [] - for (let h of have) { - for (let hash of h.lastSync) lastSyncHashes[hash] = true - bloomFilters.push(new BloomFilter(h.bloom)) - } - - // Get all changes that were added since the last sync - const changes = Backend.getChanges(backend, Object.keys(lastSyncHashes)) - .map(change => decodeChangeMeta(change, true)) - - let changeHashes = {}, dependents = {}, hashesToSend = {} - for (let change of changes) { - changeHashes[change.hash] = true - - // For each change, make a list of changes that depend on it - for (let dep of change.deps) { - if (!dependents[dep]) dependents[dep] = [] - dependents[dep].push(change.hash) - } - - // Exclude any change hashes contained in one or more Bloom filters - if (bloomFilters.every(bloom => !bloom.containsHash(change.hash))) { - hashesToSend[change.hash] = true - } - } - - // Include any changes that depend on a Bloom-negative change - let stack = Object.keys(hashesToSend) - while (stack.length > 0) { - const hash = stack.pop() - if (dependents[hash]) { - for (let dep of dependents[hash]) { - if (!hashesToSend[dep]) { - hashesToSend[dep] = true - stack.push(dep) - } - } - } - } - - // Include any explicitly requested changes - let changesToSend = [] - for (let hash of need) { - hashesToSend[hash] = true - if (!changeHashes[hash]) { // Change is not among those returned by getMissingChanges()? - const change = Backend.getChangeByHash(backend, hash) - if (change) changesToSend.push(change) - } - } - - // Return changes in the order they were returned by getMissingChanges() - for (let change of changes) { - if (hashesToSend[change.hash]) changesToSend.push(change.change) - } - return changesToSend -} - -function initSyncState() { - return { - sharedHeads: [], - lastSentHeads: [], - theirHeads: null, - theirNeed: null, - theirHave: null, - sentHashes: {}, - } -} - -function compareArrays(a, b) { - return (a.length === b.length) && a.every((v, i) => v === b[i]) -} - -/** - * Given a backend and what we believe to be the state of our peer, generate a message which tells - * them about we have and includes any changes we believe they need - */ -function generateSyncMessage(backend, syncState) { - if (!backend) { - throw new Error("generateSyncMessage called with no Automerge document") - } - if (!syncState) { - throw new Error("generateSyncMessage requires a syncState, which can be created with initSyncState()") - } - - let { sharedHeads, lastSentHeads, theirHeads, theirNeed, theirHave, sentHashes } = syncState - const ourHeads = Backend.getHeads(backend) - - // Hashes to explicitly request from the remote peer: any missing dependencies of unapplied - // changes, and any of the remote peer's heads that we don't know about - const ourNeed = Backend.getMissingDeps(backend, theirHeads || []) - - // There are two reasons why ourNeed may be nonempty: 1. we might be missing dependencies due to - // Bloom filter false positives; 2. we might be missing heads that the other peer mentioned - // because they (intentionally) only sent us a subset of changes. In case 1, we leave the `have` - // field of the message empty because we just want to fill in the missing dependencies for now. - // In case 2, or if ourNeed is empty, we send a Bloom filter to request any unsent changes. - let ourHave = [] - if (!theirHeads || ourNeed.every(hash => theirHeads.includes(hash))) { - ourHave = [makeBloomFilter(backend, sharedHeads)] - } - - // Fall back to a full re-sync if the sender's last sync state includes hashes - // that we don't know. This could happen if we crashed after the last sync and - // failed to persist changes that the other node already sent us. - if (theirHave && theirHave.length > 0) { - const lastSync = theirHave[0].lastSync - if (!lastSync.every(hash => Backend.getChangeByHash(backend, hash))) { - // we need to queue them to send us a fresh sync message, the one they sent is uninteligible so we don't know what they need - const resetMsg = {heads: ourHeads, need: [], have: [{ lastSync: [], bloom: new Uint8Array(0) }], changes: []} - return [syncState, encodeSyncMessage(resetMsg)] - } - } - - // XXX: we should limit ourselves to only sending a subset of all the messages, probably limited by a total message size - // these changes should ideally be RLE encoded but we haven't implemented that yet. - let changesToSend = Array.isArray(theirHave) && Array.isArray(theirNeed) ? getChangesToSend(backend, theirHave, theirNeed) : [] - - // If the heads are equal, we're in sync and don't need to do anything further - const headsUnchanged = Array.isArray(lastSentHeads) && compareArrays(ourHeads, lastSentHeads) - const headsEqual = Array.isArray(theirHeads) && compareArrays(ourHeads, theirHeads) - if (headsUnchanged && headsEqual && changesToSend.length === 0) { - // no need to send a sync message if we know we're synced! - return [syncState, null] - } - - // TODO: this recomputes the SHA-256 hash of each change; we should restructure this to avoid the - // unnecessary recomputation - changesToSend = changesToSend.filter(change => !sentHashes[decodeChangeMeta(change, true).hash]) - - // Regular response to a sync message: send any changes that the other node - // doesn't have. We leave the "have" field empty because the previous message - // generated by `syncStart` already indicated what changes we have. - const syncMessage = {heads: ourHeads, have: ourHave, need: ourNeed, changes: changesToSend} - if (changesToSend.length > 0) { - sentHashes = copyObject(sentHashes) - for (const change of changesToSend) { - sentHashes[decodeChangeMeta(change, true).hash] = true - } - } - - syncState = Object.assign({}, syncState, {lastSentHeads: ourHeads, sentHashes}) - return [syncState, encodeSyncMessage(syncMessage)] -} - -/** - * Computes the heads that we share with a peer after we have just received some changes from that - * peer and applied them. This may not be sufficient to bring our heads in sync with the other - * peer's heads, since they may have only sent us a subset of their outstanding changes. - * - * `myOldHeads` are the local heads before the most recent changes were applied, `myNewHeads` are - * the local heads after those changes were applied, and `ourOldSharedHeads` is the previous set of - * shared heads. Applying the changes will have replaced some heads with others, but some heads may - * have remained unchanged (because they are for branches on which no changes have been added). Any - * such unchanged heads remain in the sharedHeads. Any sharedHeads that were replaced by applying - * changes are also replaced as sharedHeads. This is safe because if we received some changes from - * another peer, that means that peer had those changes, and therefore we now both know about them. - */ -function advanceHeads(myOldHeads, myNewHeads, ourOldSharedHeads) { - const newHeads = myNewHeads.filter((head) => !myOldHeads.includes(head)) - const commonHeads = ourOldSharedHeads.filter((head) => myNewHeads.includes(head)) - const advancedHeads = [...new Set([...newHeads, ...commonHeads])].sort() - return advancedHeads -} - - -/** - * Given a backend, a message message and the state of our peer, apply any changes, update what - * we believe about the peer, and (if there were applied changes) produce a patch for the frontend - */ -function receiveSyncMessage(backend, oldSyncState, binaryMessage) { - if (!backend) { - throw new Error("generateSyncMessage called with no Automerge document") - } - if (!oldSyncState) { - throw new Error("generateSyncMessage requires a syncState, which can be created with initSyncState()") - } - - let { sharedHeads, lastSentHeads, sentHashes } = oldSyncState, patch = null - const message = decodeSyncMessage(binaryMessage) - const beforeHeads = Backend.getHeads(backend) - - // If we received changes, we try to apply them to the document. There may still be missing - // dependencies due to Bloom filter false positives, in which case the backend will enqueue the - // changes without applying them. The set of changes may also be incomplete if the sender decided - // to break a large set of changes into chunks. - if (message.changes.length > 0) { - [backend, patch] = Backend.applyChanges(backend, message.changes) - sharedHeads = advanceHeads(beforeHeads, Backend.getHeads(backend), sharedHeads) - } - - // If heads are equal, indicate we don't need to send a response message - if (message.changes.length === 0 && compareArrays(message.heads, beforeHeads)) { - lastSentHeads = message.heads - } - - // If all of the remote heads are known to us, that means either our heads are equal, or we are - // ahead of the remote peer. In this case, take the remote heads to be our shared heads. - const knownHeads = message.heads.filter(head => Backend.getChangeByHash(backend, head)) - if (knownHeads.length === message.heads.length) { - sharedHeads = message.heads - // If the remote peer has lost all its data, reset our state to perform a full resync - if (message.heads.length === 0) { - lastSentHeads = [] - sentHashes = [] - } - } else { - // If some remote heads are unknown to us, we add all the remote heads we know to - // sharedHeads, but don't remove anything from sharedHeads. This might cause sharedHeads to - // contain some redundant hashes (where one hash is actually a transitive dependency of - // another), but this will be cleared up as soon as we know all the remote heads. - sharedHeads = [...new Set(knownHeads.concat(sharedHeads))].sort() - } - - const syncState = { - sharedHeads, // what we have in common to generate an efficient bloom filter - lastSentHeads, - theirHave: message.have, // the information we need to calculate the changes they need - theirHeads: message.heads, - theirNeed: message.need, - sentHashes - } - return [backend, syncState, patch] -} - -module.exports = { - receiveSyncMessage, generateSyncMessage, - encodeSyncMessage, decodeSyncMessage, - initSyncState, encodeSyncState, decodeSyncState, - BloomFilter // BloomFilter is a private API, exported only for testing purposes -} - -},{"../src/common":85,"./backend":46,"./columnar":47,"./encoding":48}],52:[function(require,module,exports){ -function backendState(backend) { - if (backend.frozen) { - throw new Error( - 'Attempting to use an outdated Automerge document that has already been updated. ' + - 'Please use the latest document state, or call Automerge.clone() if you really ' + - 'need to use this old document state.' - ) - } - return backend.state -} - -module.exports = { - backendState -} - -},{}],53:[function(require,module,exports){ -const { isObject, copyObject, parseOpId } = require('../src/common') -const { OBJECT_ID, CONFLICTS, ELEM_IDS } = require('./constants') -const { instantiateText } = require('./text') -const { instantiateTable } = require('./table') -const { Counter } = require('./counter') - -/** - * Reconstructs the value from the patch object `patch`. - */ -function getValue(patch, object, updated) { - if (patch.objectId) { - // If the objectId of the existing object does not match the objectId in the patch, - // that means the patch is replacing the object with a new one made from scratch - if (object && object[OBJECT_ID] !== patch.objectId) { - object = undefined - } - return interpretPatch(patch, object, updated) - } else if (patch.datatype === 'timestamp') { - // Timestamp: value is milliseconds since 1970 epoch - return new Date(patch.value) - } else if (patch.datatype === 'counter') { - return new Counter(patch.value) - } else { - // Primitive value (int, uint, float64, string, boolean, or null) - return patch.value - } -} - -/** - * Compares two strings, interpreted as Lamport timestamps of the form - * 'counter@actorId'. Returns 1 if ts1 is greater, or -1 if ts2 is greater. - */ -function lamportCompare(ts1, ts2) { - const regex = /^(\d+)@(.*)$/ - const time1 = regex.test(ts1) ? parseOpId(ts1) : {counter: 0, actorId: ts1} - const time2 = regex.test(ts2) ? parseOpId(ts2) : {counter: 0, actorId: ts2} - if (time1.counter < time2.counter) return -1 - if (time1.counter > time2.counter) return 1 - if (time1.actorId < time2.actorId) return -1 - if (time1.actorId > time2.actorId) return 1 - return 0 -} - -/** - * `props` is an object of the form: - * `{key1: {opId1: {...}, opId2: {...}}, key2: {opId3: {...}}}` - * where the outer object is a mapping from property names to inner objects, - * and the inner objects are a mapping from operation ID to sub-patch. - * This function interprets that structure and updates the objects `object` and - * `conflicts` to reflect it. For each key, the greatest opId (by Lamport TS - * order) is chosen as the default resolution; that op's value is assigned - * to `object[key]`. Moreover, all the opIds and values are packed into a - * conflicts object of the form `{opId1: value1, opId2: value2}` and assigned - * to `conflicts[key]`. If there is no conflict, the conflicts object contains - * just a single opId-value mapping. - */ -function applyProperties(props, object, conflicts, updated) { - if (!props) return - - for (let key of Object.keys(props)) { - const values = {}, opIds = Object.keys(props[key]).sort(lamportCompare).reverse() - for (let opId of opIds) { - const subpatch = props[key][opId] - if (conflicts[key] && conflicts[key][opId]) { - values[opId] = getValue(subpatch, conflicts[key][opId], updated) - } else { - values[opId] = getValue(subpatch, undefined, updated) - } - } - - if (opIds.length === 0) { - delete object[key] - delete conflicts[key] - } else { - object[key] = values[opIds[0]] - conflicts[key] = values - } - } -} - -/** - * Creates a writable copy of an immutable map object. If `originalObject` - * is undefined, creates an empty object with ID `objectId`. - */ -function cloneMapObject(originalObject, objectId) { - const object = copyObject(originalObject) - const conflicts = copyObject(originalObject ? originalObject[CONFLICTS] : undefined) - Object.defineProperty(object, OBJECT_ID, {value: objectId}) - Object.defineProperty(object, CONFLICTS, {value: conflicts}) - return object -} - -/** - * Updates the map object `obj` according to the modifications described in - * `patch`, or creates a new object if `obj` is undefined. Mutates `updated` - * to map the objectId to the new object, and returns the new object. - */ -function updateMapObject(patch, obj, updated) { - const objectId = patch.objectId - if (!updated[objectId]) { - updated[objectId] = cloneMapObject(obj, objectId) - } - - const object = updated[objectId] - applyProperties(patch.props, object, object[CONFLICTS], updated) - return object -} - -/** - * Updates the table object `obj` according to the modifications described in - * `patch`, or creates a new object if `obj` is undefined. Mutates `updated` - * to map the objectId to the new object, and returns the new object. - */ -function updateTableObject(patch, obj, updated) { - const objectId = patch.objectId - if (!updated[objectId]) { - updated[objectId] = obj ? obj._clone() : instantiateTable(objectId) - } - - const object = updated[objectId] - - for (let key of Object.keys(patch.props || {})) { - const opIds = Object.keys(patch.props[key]) - - if (opIds.length === 0) { - object.remove(key) - } else if (opIds.length === 1) { - const subpatch = patch.props[key][opIds[0]] - object._set(key, getValue(subpatch, object.byId(key), updated), opIds[0]) - } else { - throw new RangeError('Conflicts are not supported on properties of a table') - } - } - return object -} - -/** - * Creates a writable copy of an immutable list object. If `originalList` is - * undefined, creates an empty list with ID `objectId`. - */ -function cloneListObject(originalList, objectId) { - const list = originalList ? originalList.slice() : [] // slice() makes a shallow clone - const conflicts = (originalList && originalList[CONFLICTS]) ? originalList[CONFLICTS].slice() : [] - const elemIds = (originalList && originalList[ELEM_IDS]) ? originalList[ELEM_IDS].slice() : [] - Object.defineProperty(list, OBJECT_ID, {value: objectId}) - Object.defineProperty(list, CONFLICTS, {value: conflicts}) - Object.defineProperty(list, ELEM_IDS, {value: elemIds}) - return list -} - -/** - * Updates the list object `obj` according to the modifications described in - * `patch`, or creates a new object if `obj` is undefined. Mutates `updated` - * to map the objectId to the new object, and returns the new object. - */ -function updateListObject(patch, obj, updated) { - const objectId = patch.objectId - if (!updated[objectId]) { - updated[objectId] = cloneListObject(obj, objectId) - } - - const list = updated[objectId], conflicts = list[CONFLICTS], elemIds = list[ELEM_IDS] - for (let i = 0; i < patch.edits.length; i++) { - const edit = patch.edits[i] - - if (edit.action === 'insert' || edit.action === 'update') { - const oldValue = conflicts[edit.index] && conflicts[edit.index][edit.opId] - let lastValue = getValue(edit.value, oldValue, updated) - let values = {[edit.opId]: lastValue} - - // Successive updates for the same index are an indication of a conflict on that list element. - // Edits are sorted in increasing order by Lamport timestamp, so the last value (with the - // greatest timestamp) is the default resolution of the conflict. - while (i < patch.edits.length - 1 && patch.edits[i + 1].index === edit.index && - patch.edits[i + 1].action === 'update') { - i++ - const conflict = patch.edits[i] - const oldValue2 = conflicts[conflict.index] && conflicts[conflict.index][conflict.opId] - lastValue = getValue(conflict.value, oldValue2, updated) - values[conflict.opId] = lastValue - } - - if (edit.action === 'insert') { - list.splice(edit.index, 0, lastValue) - conflicts.splice(edit.index, 0, values) - elemIds.splice(edit.index, 0, edit.elemId) - } else { - list[edit.index] = lastValue - conflicts[edit.index] = values - } - - } else if (edit.action === 'multi-insert') { - const startElemId = parseOpId(edit.elemId), newElems = [], newValues = [], newConflicts = [] - const datatype = edit.datatype - edit.values.forEach((value, index) => { - const elemId = `${startElemId.counter + index}@${startElemId.actorId}` - value = getValue({ value, datatype }, undefined, updated) - newValues.push(value) - newConflicts.push({[elemId]: {value, datatype, type: 'value'}}) - newElems.push(elemId) - }) - list.splice(edit.index, 0, ...newValues) - conflicts.splice(edit.index, 0, ...newConflicts) - elemIds.splice(edit.index, 0, ...newElems) - - } else if (edit.action === 'remove') { - list.splice(edit.index, edit.count) - conflicts.splice(edit.index, edit.count) - elemIds.splice(edit.index, edit.count) - } - } - return list -} - -/** - * Updates the text object `obj` according to the modifications described in - * `patch`, or creates a new object if `obj` is undefined. Mutates `updated` - * to map the objectId to the new object, and returns the new object. - */ -function updateTextObject(patch, obj, updated) { - const objectId = patch.objectId - let elems - if (updated[objectId]) { - elems = updated[objectId].elems - } else if (obj) { - elems = obj.elems.slice() - } else { - elems = [] - } - - for (const edit of patch.edits) { - if (edit.action === 'insert') { - const value = getValue(edit.value, undefined, updated) - const elem = {elemId: edit.elemId, pred: [edit.opId], value} - elems.splice(edit.index, 0, elem) - - } else if (edit.action === 'multi-insert') { - const startElemId = parseOpId(edit.elemId) - const datatype = edit.datatype - const newElems = edit.values.map((value, index) => { - value = getValue({ datatype, value }, undefined, updated) - const elemId = `${startElemId.counter + index}@${startElemId.actorId}` - return {elemId, pred: [elemId], value} - }) - elems.splice(edit.index, 0, ...newElems) - - } else if (edit.action === 'update') { - const elemId = elems[edit.index].elemId - const value = getValue(edit.value, elems[edit.index].value, updated) - elems[edit.index] = {elemId, pred: [edit.opId], value} - - } else if (edit.action === 'remove') { - elems.splice(edit.index, edit.count) - } - } - - updated[objectId] = instantiateText(objectId, elems) - return updated[objectId] -} - -/** - * Applies the patch object `patch` to the read-only document object `obj`. - * Clones a writable copy of `obj` and places it in `updated` (indexed by - * objectId), if that has not already been done. Returns the updated object. - */ -function interpretPatch(patch, obj, updated) { - // Return original object if it already exists and isn't being modified - if (isObject(obj) && (!patch.props || Object.keys(patch.props).length === 0) && - (!patch.edits || patch.edits.length === 0) && !updated[patch.objectId]) { - return obj - } - - if (patch.type === 'map') { - return updateMapObject(patch, obj, updated) - } else if (patch.type === 'table') { - return updateTableObject(patch, obj, updated) - } else if (patch.type === 'list') { - return updateListObject(patch, obj, updated) - } else if (patch.type === 'text') { - return updateTextObject(patch, obj, updated) - } else { - throw new TypeError(`Unknown object type: ${patch.type}`) - } -} - -/** - * Creates a writable copy of the immutable document root object `root`. - */ -function cloneRootObject(root) { - if (root[OBJECT_ID] !== '_root') { - throw new RangeError(`Not the root object: ${root[OBJECT_ID]}`) - } - return cloneMapObject(root, '_root') -} - -module.exports = { - interpretPatch, cloneRootObject -} - -},{"../src/common":85,"./constants":54,"./counter":56,"./table":61,"./text":62}],54:[function(require,module,exports){ -// Properties of the document root object -const OPTIONS = Symbol('_options') // object containing options passed to init() -const CACHE = Symbol('_cache') // map from objectId to immutable object -const STATE = Symbol('_state') // object containing metadata about current state (e.g. sequence numbers) - -// Properties of all Automerge objects -const OBJECT_ID = Symbol('_objectId') // the object ID of the current object (string) -const CONFLICTS = Symbol('_conflicts') // map or list (depending on object type) of conflicts -const CHANGE = Symbol('_change') // the context object on proxy objects used in change callback -const ELEM_IDS = Symbol('_elemIds') // list containing the element ID of each list element - -module.exports = { - OPTIONS, CACHE, STATE, OBJECT_ID, CONFLICTS, CHANGE, ELEM_IDS -} - -},{}],55:[function(require,module,exports){ -const { CACHE, OBJECT_ID, CONFLICTS, ELEM_IDS, STATE } = require('./constants') -const { interpretPatch } = require('./apply_patch') -const { Text } = require('./text') -const { Table } = require('./table') -const { Counter, getWriteableCounter } = require('./counter') -const { Int, Uint, Float64 } = require('./numbers') -const { isObject, parseOpId, createArrayOfNulls } = require('../src/common') -const uuid = require('../src/uuid') - - -/** - * An instance of this class is passed to `rootObjectProxy()`. The methods are - * called by proxy object mutation functions to query the current object state - * and to apply the requested changes. - */ -class Context { - constructor (doc, actorId, applyPatch) { - this.actorId = actorId - this.nextOpNum = doc[STATE].maxOp + 1 - this.cache = doc[CACHE] - this.updated = {} - this.ops = [] - this.applyPatch = applyPatch ? applyPatch : interpretPatch - } - - /** - * Adds an operation object to the list of changes made in the current context. - */ - addOp(operation) { - this.ops.push(operation) - - if (operation.action === 'set' && operation.values) { - this.nextOpNum += operation.values.length - } else if (operation.action === 'del' && operation.multiOp) { - this.nextOpNum += operation.multiOp - } else { - this.nextOpNum += 1 - } - } - - /** - * Returns the operation ID of the next operation to be added to the context. - */ - nextOpId() { - return `${this.nextOpNum}@${this.actorId}` - } - - /** - * Takes a value and returns an object describing the value (in the format used by patches). - */ - getValueDescription(value) { - if (!['object', 'boolean', 'number', 'string'].includes(typeof value)) { - throw new TypeError(`Unsupported type of value: ${typeof value}`) - } - - if (isObject(value)) { - if (value instanceof Date) { - // Date object, represented as milliseconds since epoch - return {type: 'value', value: value.getTime(), datatype: 'timestamp'} - - } else if (value instanceof Int) { - return {type: 'value', value: value.value, datatype: 'int'} - } else if (value instanceof Uint) { - return {type: 'value', value: value.value, datatype: 'uint'} - } else if (value instanceof Float64) { - return {type: 'value', value: value.value, datatype: 'float64'} - } else if (value instanceof Counter) { - // Counter object - return {type: 'value', value: value.value, datatype: 'counter'} - - } else { - // Nested object (map, list, text, or table) - const objectId = value[OBJECT_ID], type = this.getObjectType(objectId) - if (!objectId) { - throw new RangeError(`Object ${JSON.stringify(value)} has no objectId`) - } - if (type === 'list' || type === 'text') { - return {objectId, type, edits: []} - } else { - return {objectId, type, props: {}} - } - } - } else if (typeof value === 'number') { - if (Number.isInteger(value) && value <= Number.MAX_SAFE_INTEGER && value >= Number.MIN_SAFE_INTEGER) { - return {type: 'value', value, datatype: 'int'} - } else { - return {type: 'value', value, datatype: 'float64'} - } - } else { - // Primitive value (string, boolean, or null) - return {type: 'value', value} - } - } - - /** - * Builds the values structure describing a single property in a patch. Finds all the values of - * property `key` of `object` (there might be multiple values in the case of a conflict), and - * returns an object that maps operation IDs to descriptions of values. - */ - getValuesDescriptions(path, object, key) { - if (object instanceof Table) { - // Table objects don't have conflicts, since rows are identified by their unique objectId - const value = object.byId(key) - return value ? {[key]: this.getValueDescription(value)} : {} - } else if (object instanceof Text) { - // Text objects don't support conflicts - const value = object.get(key) - const elemId = object.getElemId(key) - return value ? {[elemId]: this.getValueDescription(value)} : {} - } else { - // Map or list objects - const conflicts = object[CONFLICTS][key], values = {} - if (!conflicts) { - throw new RangeError(`No children at key ${key} of path ${JSON.stringify(path)}`) - } - for (let opId of Object.keys(conflicts)) { - values[opId] = this.getValueDescription(conflicts[opId]) - } - return values - } - } - - /** - * Returns the value at property `key` of object `object`. In the case of a conflict, returns - * the value whose assignment operation has the ID `opId`. - */ - getPropertyValue(object, key, opId) { - if (object instanceof Table) { - return object.byId(key) - } else if (object instanceof Text) { - return object.get(key) - } else { - return object[CONFLICTS][key][opId] - } - } - - /** - * Recurses along `path` into the patch object `patch`, creating nodes along the way as needed - * by mutating the patch object. Returns the subpatch at the given path. - */ - getSubpatch(patch, path) { - if (path.length == 0) return patch - let subpatch = patch, object = this.getObject('_root') - - for (let pathElem of path) { - let values = this.getValuesDescriptions(path, object, pathElem.key) - if (subpatch.props) { - if (!subpatch.props[pathElem.key]) { - subpatch.props[pathElem.key] = values - } - } else if (subpatch.edits) { - for (const opId of Object.keys(values)) { - subpatch.edits.push({action: 'update', index: pathElem.key, opId, value: values[opId]}) - } - } - - let nextOpId = null - for (let opId of Object.keys(values)) { - if (values[opId].objectId === pathElem.objectId) { - nextOpId = opId - } - } - if (!nextOpId) { - throw new RangeError(`Cannot find path object with objectId ${pathElem.objectId}`) - } - - subpatch = values[nextOpId] - object = this.getPropertyValue(object, pathElem.key, nextOpId) - } - - return subpatch - } - - /** - * Returns an object (not proxied) from the cache or updated set, as appropriate. - */ - getObject(objectId) { - const object = this.updated[objectId] || this.cache[objectId] - if (!object) throw new RangeError(`Target object does not exist: ${objectId}`) - return object - } - - /** - * Returns a string that is either 'map', 'table', 'list', or 'text', indicating - * the type of the object with ID `objectId`. - */ - getObjectType(objectId) { - if (objectId === '_root') return 'map' - const object = this.getObject(objectId) - if (object instanceof Text) return 'text' - if (object instanceof Table) return 'table' - if (Array.isArray(object)) return 'list' - return 'map' - } - - /** - * Returns the value associated with the property named `key` on the object - * at path `path`. If the value is an object, returns a proxy for it. - */ - getObjectField(path, objectId, key) { - if (!['string', 'number'].includes(typeof key)) return - const object = this.getObject(objectId) - - if (object[key] instanceof Counter) { - return getWriteableCounter(object[key].value, this, path, objectId, key) - - } else if (isObject(object[key])) { - const childId = object[key][OBJECT_ID] - const subpath = path.concat([{key, objectId: childId}]) - // The instantiateObject function is added to the context object by rootObjectProxy() - return this.instantiateObject(subpath, childId) - - } else { - return object[key] - } - } - - /** - * Recursively creates Automerge versions of all the objects and nested objects in `value`, - * constructing a patch and operations that describe the object tree. The new object is - * assigned to the property `key` in the object with ID `obj`. If the object is a list or - * text, `key` must be set to the list index being updated, and `elemId` must be set to the - * elemId of the element being updated. If `insert` is true, we insert a new list element - * (or text character) at index `key`, and `elemId` must be the elemId of the immediate - * predecessor element (or the string '_head' if inserting at index 0). If the assignment - * overwrites a previous value at this key/element, `pred` must be set to the array of the - * prior operations we are overwriting (empty array if there is no existing value). - */ - createNestedObjects(obj, key, value, insert, pred, elemId) { - if (value[OBJECT_ID]) { - throw new RangeError('Cannot create a reference to an existing document object') - } - const objectId = this.nextOpId() - - if (value instanceof Text) { - // Create a new Text object - this.addOp(elemId ? {action: 'makeText', obj, elemId, insert, pred} - : {action: 'makeText', obj, key, insert, pred}) - const subpatch = {objectId, type: 'text', edits: []} - this.insertListItems(subpatch, 0, [...value], true) - return subpatch - - } else if (value instanceof Table) { - // Create a new Table object - if (value.count > 0) { - throw new RangeError('Assigning a non-empty Table object is not supported') - } - this.addOp(elemId ? {action: 'makeTable', obj, elemId, insert, pred} - : {action: 'makeTable', obj, key, insert, pred}) - return {objectId, type: 'table', props: {}} - - } else if (Array.isArray(value)) { - // Create a new list object - this.addOp(elemId ? {action: 'makeList', obj, elemId, insert, pred} - : {action: 'makeList', obj, key, insert, pred}) - const subpatch = {objectId, type: 'list', edits: []} - this.insertListItems(subpatch, 0, value, true) - return subpatch - - } else { - // Create a new map object - this.addOp(elemId ? {action: 'makeMap', obj, elemId, insert, pred} - : {action: 'makeMap', obj, key, insert, pred}) - let props = {} - for (let nested of Object.keys(value).sort()) { - const opId = this.nextOpId() - const valuePatch = this.setValue(objectId, nested, value[nested], false, []) - props[nested] = {[opId]: valuePatch} - } - return {objectId, type: 'map', props} - } - } - - /** - * Records an assignment to a particular key in a map, or a particular index in a list. - * `objectId` is the ID of the object being modified, `key` is the property name or list - * index being updated, and `value` is the new value being assigned. If `insert` is true, - * a new list element is inserted at index `key`, and `value` is assigned to that new list - * element. `pred` is an array of opIds for previous values of the property being assigned, - * which are overwritten by this operation. If the object being modified is a list or text, - * `elemId` is the element ID of the list element being updated (if insert=false), or the - * element ID of the list element immediately preceding the insertion (if insert=true). - * - * Returns a patch describing the new value. The return value is of the form - * `{objectId, type, props}` if `value` is an object, or `{value, datatype}` if it is a - * primitive value. For string, number, boolean, or null the datatype is omitted. - */ - setValue(objectId, key, value, insert, pred, elemId) { - if (!objectId) { - throw new RangeError('setValue needs an objectId') - } - if (key === '') { - throw new RangeError('The key of a map entry must not be an empty string') - } - - if (isObject(value) && !(value instanceof Date) && !(value instanceof Counter) && !(value instanceof Int) && !(value instanceof Uint) && !(value instanceof Float64)) { - // Nested object (map, list, text, or table) - return this.createNestedObjects(objectId, key, value, insert, pred, elemId) - } else { - // Date or counter object, or primitive value (number, string, boolean, or null) - const description = this.getValueDescription(value) - const op = {action: 'set', obj: objectId, insert, value: description.value, pred} - if (elemId) op.elemId = elemId; else op.key = key - if (description.datatype) op.datatype = description.datatype - this.addOp(op) - return description - } - } - - /** - * Constructs a new patch, calls `callback` with the subpatch at the location `path`, - * and then immediately applies the patch to the document. - */ - applyAtPath(path, callback) { - let diff = {objectId: '_root', type: 'map', props: {}} - callback(this.getSubpatch(diff, path)) - this.applyPatch(diff, this.cache._root, this.updated) - } - - /** - * Updates the map object at path `path`, setting the property with name - * `key` to `value`. - */ - setMapKey(path, key, value) { - if (typeof key !== 'string') { - throw new RangeError(`The key of a map entry must be a string, not ${typeof key}`) - } - - const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId - const object = this.getObject(objectId) - if (object[key] instanceof Counter) { - throw new RangeError('Cannot overwrite a Counter object; use .increment() or .decrement() to change its value.') - } - - // If the assigned field value is the same as the existing value, and - // the assignment does not resolve a conflict, do nothing - if (object[key] !== value || Object.keys(object[CONFLICTS][key] || {}).length > 1 || value === undefined) { - this.applyAtPath(path, subpatch => { - const pred = getPred(object, key) - const opId = this.nextOpId() - const valuePatch = this.setValue(objectId, key, value, false, pred) - subpatch.props[key] = {[opId]: valuePatch} - }) - } - } - - /** - * Updates the map object at path `path`, deleting the property `key`. - */ - deleteMapKey(path, key) { - const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId - const object = this.getObject(objectId) - - if (object[key] !== undefined) { - const pred = getPred(object, key) - this.addOp({action: 'del', obj: objectId, key, insert: false, pred}) - this.applyAtPath(path, subpatch => { - subpatch.props[key] = {} - }) - } - } - - /** - * Inserts a sequence of new list elements `values` into a list, starting at position `index`. - * `newObject` is true if we are creating a new list object, and false if we are updating an - * existing one. `subpatch` is the patch for the list object being modified. Mutates - * `subpatch` to reflect the sequence of values. - */ - insertListItems(subpatch, index, values, newObject) { - const list = newObject ? [] : this.getObject(subpatch.objectId) - if (index < 0 || index > list.length) { - throw new RangeError(`List index ${index} is out of bounds for list of length ${list.length}`) - } - if (values.length === 0) return - - let elemId = getElemId(list, index, true) - const allPrimitive = values.every(v => typeof v === 'string' || typeof v === 'number' || - typeof v === 'boolean' || v === null || - (isObject(v) && (v instanceof Date || v instanceof Counter || v instanceof Int || - v instanceof Uint || v instanceof Float64))) - const allValueDescriptions = allPrimitive ? values.map(v => this.getValueDescription(v)) : [] - const allDatatypesSame = allValueDescriptions.every(t => t.datatype === allValueDescriptions[0].datatype) - - if (allPrimitive && allDatatypesSame && values.length > 1) { - const nextElemId = this.nextOpId() - const datatype = allValueDescriptions[0].datatype - const values = allValueDescriptions.map(v => v.value) - const op = {action: 'set', obj: subpatch.objectId, elemId, insert: true, values, pred: []} - const edit = {action: 'multi-insert', elemId: nextElemId, index, values} - if (datatype) { - op.datatype = datatype - edit.datatype = datatype - } - this.addOp(op) - subpatch.edits.push(edit) - } else { - for (let offset = 0; offset < values.length; offset++) { - let nextElemId = this.nextOpId() - const valuePatch = this.setValue(subpatch.objectId, index + offset, values[offset], true, [], elemId) - elemId = nextElemId - subpatch.edits.push({action: 'insert', index: index + offset, elemId, opId: elemId, value: valuePatch}) - } - } - } - - /** - * Updates the list object at path `path`, replacing the current value at - * position `index` with the new value `value`. - */ - setListIndex(path, index, value) { - const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId - const list = this.getObject(objectId) - - // Assignment past the end of the list => insert nulls followed by new value - if (index >= list.length) { - const insertions = createArrayOfNulls(index - list.length) - insertions.push(value) - return this.splice(path, list.length, 0, insertions) - } - if (list[index] instanceof Counter) { - throw new RangeError('Cannot overwrite a Counter object; use .increment() or .decrement() to change its value.') - } - - // If the assigned list element value is the same as the existing value, and - // the assignment does not resolve a conflict, do nothing - if (list[index] !== value || Object.keys(list[CONFLICTS][index] || {}).length > 1 || value === undefined) { - this.applyAtPath(path, subpatch => { - const pred = getPred(list, index) - const opId = this.nextOpId() - const valuePatch = this.setValue(objectId, index, value, false, pred, getElemId(list, index)) - subpatch.edits.push({action: 'update', index, opId, value: valuePatch}) - }) - } - } - - /** - * Updates the list object at path `path`, deleting `deletions` list elements starting from - * list index `start`, and inserting the list of new elements `insertions` at that position. - */ - splice(path, start, deletions, insertions) { - const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId - let list = this.getObject(objectId) - if (start < 0 || deletions < 0 || start > list.length - deletions) { - throw new RangeError(`${deletions} deletions starting at index ${start} are out of bounds for list of length ${list.length}`) - } - if (deletions === 0 && insertions.length === 0) return - - let patch = {diffs: {objectId: '_root', type: 'map', props: {}}} - let subpatch = this.getSubpatch(patch.diffs, path) - - if (deletions > 0) { - let op, lastElemParsed, lastPredParsed - for (let i = 0; i < deletions; i++) { - if (this.getObjectField(path, objectId, start + i) instanceof Counter) { - // This may seem bizarre, but it's really fiddly to implement deletion of counters from - // lists, and I doubt anyone ever needs to do this, so I'm just going to throw an - // exception for now. The reason is: a counter is created by a set operation with counter - // datatype, and subsequent increment ops are successors to the set operation. Normally, a - // set operation with successor indicates a value that has been overwritten, so a set - // operation with successors is normally invisible. Counters are an exception, because the - // increment operations don't make the set operation invisible. When a counter appears in - // a map, this is not too bad: if all successors are increments, then the counter remains - // visible; if one or more successors are deletions, it goes away. However, when deleting - // a list element, we have the additional challenge that we need to distinguish between a - // list element that is being deleted by the current change (in which case we need to put - // a 'remove' action in the patch's edits for that list) and a list element that was - // already deleted previously (in which case the patch should not reflect the deletion). - // This can be done, but as I said, it's fiddly. If someone wants to pick this up in the - // future, hopefully the above description will be enough to get you started. Good luck! - throw new TypeError('Unsupported operation: deleting a counter from a list') - } - - // Any sequences of deletions with consecutive elemId and pred values get combined into a - // single multiOp; any others become individual deletion operations. This optimisation only - // kicks in if the user deletes a sequence of elements at once (in a single call to splice); - // it might be nice to also detect such runs of deletions in the case where the user deletes - // a sequence of list elements one by one. - const thisElem = getElemId(list, start + i), thisElemParsed = parseOpId(thisElem) - const thisPred = getPred(list, start + i) - const thisPredParsed = (thisPred.length === 1) ? parseOpId(thisPred[0]) : undefined - - if (op && lastElemParsed && lastPredParsed && thisPredParsed && - lastElemParsed.actorId === thisElemParsed.actorId && lastElemParsed.counter + 1 === thisElemParsed.counter && - lastPredParsed.actorId === thisPredParsed.actorId && lastPredParsed.counter + 1 === thisPredParsed.counter) { - op.multiOp = (op.multiOp || 1) + 1 - } else { - if (op) this.addOp(op) - op = {action: 'del', obj: objectId, elemId: thisElem, insert: false, pred: thisPred} - } - lastElemParsed = thisElemParsed - lastPredParsed = thisPredParsed - } - this.addOp(op) - subpatch.edits.push({action: 'remove', index: start, count: deletions}) - } - - if (insertions.length > 0) { - this.insertListItems(subpatch, start, insertions, false) - } - this.applyPatch(patch.diffs, this.cache._root, this.updated) - } - - /** - * Updates the table object at path `path`, adding a new entry `row`. - * Returns the objectId of the new row. - */ - addTableRow(path, row) { - if (!isObject(row) || Array.isArray(row)) { - throw new TypeError('A table row must be an object') - } - if (row[OBJECT_ID]) { - throw new TypeError('Cannot reuse an existing object as table row') - } - if (row.id) { - throw new TypeError('A table row must not have an "id" property; it is generated automatically') - } - - const id = uuid() - const valuePatch = this.setValue(path[path.length - 1].objectId, id, row, false, []) - this.applyAtPath(path, subpatch => { - subpatch.props[id] = {[valuePatch.objectId]: valuePatch} - }) - return id - } - - /** - * Updates the table object at path `path`, deleting the row with ID `rowId`. - * `pred` is the opId of the operation that originally created the row. - */ - deleteTableRow(path, rowId, pred) { - const objectId = path[path.length - 1].objectId, table = this.getObject(objectId) - - if (table.byId(rowId)) { - this.addOp({action: 'del', obj: objectId, key: rowId, insert: false, pred: [pred]}) - this.applyAtPath(path, subpatch => { - subpatch.props[rowId] = {} - }) - } - } - - /** - * Adds the integer `delta` to the value of the counter located at property - * `key` in the object at path `path`. - */ - increment(path, key, delta) { - const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId - const object = this.getObject(objectId) - if (!(object[key] instanceof Counter)) { - throw new TypeError('Only counter values can be incremented') - } - - // TODO what if there is a conflicting value on the same key as the counter? - const type = this.getObjectType(objectId) - const value = object[key].value + delta - const opId = this.nextOpId() - const pred = getPred(object, key) - - if (type === 'list' || type === 'text') { - const elemId = getElemId(object, key, false) - this.addOp({action: 'inc', obj: objectId, elemId, value: delta, insert: false, pred}) - } else { - this.addOp({action: 'inc', obj: objectId, key, value: delta, insert: false, pred}) - } - - this.applyAtPath(path, subpatch => { - if (type === 'list' || type === 'text') { - subpatch.edits.push({action: 'update', index: key, opId, value: {value, datatype: 'counter'}}) - } else { - subpatch.props[key] = {[opId]: {value, datatype: 'counter'}} - } - }) - } -} - -function getPred(object, key) { - if (object instanceof Table) { - return [object.opIds[key]] - } else if (object instanceof Text) { - return object.elems[key].pred - } else if (object[CONFLICTS]) { - return object[CONFLICTS][key] ? Object.keys(object[CONFLICTS][key]) : [] - } else { - return [] - } -} - -function getElemId(list, index, insert = false) { - if (insert) { - if (index === 0) return '_head' - index -= 1 - } - if (list[ELEM_IDS]) return list[ELEM_IDS][index] - if (list.getElemId) return list.getElemId(index) - throw new RangeError(`Cannot find elemId at list index ${index}`) -} - -module.exports = { - Context -} - -},{"../src/common":85,"../src/uuid":86,"./apply_patch":53,"./constants":54,"./counter":56,"./numbers":58,"./table":61,"./text":62}],56:[function(require,module,exports){ -/** - * The most basic CRDT: an integer value that can be changed only by - * incrementing and decrementing. Since addition of integers is commutative, - * the value trivially converges. - */ -class Counter { - constructor(value) { - this.value = value || 0 - Object.freeze(this) - } - - /** - * A peculiar JavaScript language feature from its early days: if the object - * `x` has a `valueOf()` method that returns a number, you can use numerical - * operators on the object `x` directly, such as `x + 1` or `x < 4`. - * This method is also called when coercing a value to a string by - * concatenating it with another string, as in `x + ''`. - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf - */ - valueOf() { - return this.value - } - - /** - * Returns the counter value as a decimal string. If `x` is a counter object, - * this method is called e.g. when you do `['value: ', x].join('')` or when - * you use string interpolation: `value: ${x}`. - */ - toString() { - return this.valueOf().toString() - } - - /** - * Returns the counter value, so that a JSON serialization of an Automerge - * document represents the counter simply as an integer. - */ - toJSON() { - return this.value - } -} - -/** - * An instance of this class is used when a counter is accessed within a change - * callback. - */ -class WriteableCounter extends Counter { - /** - * Increases the value of the counter by `delta`. If `delta` is not given, - * increases the value of the counter by 1. - */ - increment(delta) { - delta = typeof delta === 'number' ? delta : 1 - this.context.increment(this.path, this.key, delta) - this.value += delta - return this.value - } - - /** - * Decreases the value of the counter by `delta`. If `delta` is not given, - * decreases the value of the counter by 1. - */ - decrement(delta) { - return this.increment(typeof delta === 'number' ? -delta : -1) - } -} - -/** - * Returns an instance of `WriteableCounter` for use in a change callback. - * `context` is the proxy context that keeps track of the mutations. - * `objectId` is the ID of the object containing the counter, and `key` is - * the property name (key in map, or index in list) where the counter is - * located. -*/ -function getWriteableCounter(value, context, path, objectId, key) { - const instance = Object.create(WriteableCounter.prototype) - instance.value = value - instance.context = context - instance.path = path - instance.objectId = objectId - instance.key = key - return instance -} - -module.exports = { Counter, getWriteableCounter } - -},{}],57:[function(require,module,exports){ -const { OPTIONS, CACHE, STATE, OBJECT_ID, CONFLICTS, CHANGE, ELEM_IDS } = require('./constants') -const { isObject, copyObject } = require('../src/common') -const uuid = require('../src/uuid') -const { interpretPatch, cloneRootObject } = require('./apply_patch') -const { rootObjectProxy } = require('./proxies') -const { Context } = require('./context') -const { Text } = require('./text') -const { Table } = require('./table') -const { Counter } = require('./counter') -const { Float64, Int, Uint } = require('./numbers') -const { Observable } = require('./observable') - -/** - * Actor IDs must consist only of hexadecimal digits so that they can be encoded - * compactly in binary form. - */ -function checkActorId(actorId) { - if (typeof actorId !== 'string') { - throw new TypeError(`Unsupported type of actorId: ${typeof actorId}`) - } - if (!/^[0-9a-f]+$/.test(actorId)) { - throw new RangeError('actorId must consist only of lowercase hex digits') - } - if (actorId.length % 2 !== 0) { - throw new RangeError('actorId must consist of an even number of digits') - } -} - -/** - * Takes a set of objects that have been updated (in `updated`) and an updated state object - * `state`, and returns a new immutable document root object based on `doc` that reflects - * those updates. - */ -function updateRootObject(doc, updated, state) { - let newDoc = updated._root - if (!newDoc) { - newDoc = cloneRootObject(doc[CACHE]._root) - updated._root = newDoc - } - Object.defineProperty(newDoc, OPTIONS, {value: doc[OPTIONS]}) - Object.defineProperty(newDoc, CACHE, {value: updated}) - Object.defineProperty(newDoc, STATE, {value: state}) - - if (doc[OPTIONS].freeze) { - for (let objectId of Object.keys(updated)) { - if (updated[objectId] instanceof Table) { - updated[objectId]._freeze() - } else if (updated[objectId] instanceof Text) { - Object.freeze(updated[objectId].elems) - Object.freeze(updated[objectId]) - } else { - Object.freeze(updated[objectId]) - Object.freeze(updated[objectId][CONFLICTS]) - } - } - } - - for (let objectId of Object.keys(doc[CACHE])) { - if (!updated[objectId]) { - updated[objectId] = doc[CACHE][objectId] - } - } - - if (doc[OPTIONS].freeze) { - Object.freeze(updated) - } - return newDoc -} - -/** - * Adds a new change request to the list of pending requests, and returns an - * updated document root object. - * The details of the change are taken from the context object `context`. - * `options` contains properties that may affect how the change is processed; in - * particular, the `message` property of `options` is an optional human-readable - * string describing the change. - */ -function makeChange(doc, context, options) { - const actor = getActorId(doc) - if (!actor) { - throw new Error('Actor ID must be initialized with setActorId() before making a change') - } - const state = copyObject(doc[STATE]) - state.seq += 1 - - const change = { - actor, - seq: state.seq, - startOp: state.maxOp + 1, - deps: state.deps, - time: (options && typeof options.time === 'number') ? options.time - : Math.round(new Date().getTime() / 1000), - message: (options && typeof options.message === 'string') ? options.message : '', - ops: context.ops - } - - if (doc[OPTIONS].backend) { - const [backendState, patch, binaryChange] = doc[OPTIONS].backend.applyLocalChange(state.backendState, change) - state.backendState = backendState - state.lastLocalChange = binaryChange - // NOTE: When performing a local change, the patch is effectively applied twice -- once by the - // context invoking interpretPatch as soon as any change is made, and the second time here - // (after a round-trip through the backend). This is perhaps more robust, as changes only take - // effect in the form processed by the backend, but the downside is a performance cost. - // Should we change this? - const newDoc = applyPatchToDoc(doc, patch, state, true) - const patchCallback = options && options.patchCallback || doc[OPTIONS].patchCallback - if (patchCallback) patchCallback(patch, doc, newDoc, true, [binaryChange]) - return [newDoc, change] - - } else { - const queuedRequest = {actor, seq: change.seq, before: doc} - state.requests = state.requests.concat([queuedRequest]) - state.maxOp = state.maxOp + countOps(change.ops) - state.deps = [] - return [updateRootObject(doc, context ? context.updated : {}, state), change] - } -} - -function countOps(ops) { - let count = 0 - for (const op of ops) { - if (op.action === 'set' && op.values) { - count += op.values.length - } else { - count += 1 - } - } - return count -} - -/** - * Returns the binary encoding of the last change made by the local actor. - */ -function getLastLocalChange(doc) { - return doc[STATE] && doc[STATE].lastLocalChange ? doc[STATE].lastLocalChange : null -} - -/** - * Applies the changes described in `patch` to the document with root object - * `doc`. The state object `state` is attached to the new root object. - * `fromBackend` should be set to `true` if the patch came from the backend, - * and to `false` if the patch is a transient local (optimistically applied) - * change from the frontend. - */ -function applyPatchToDoc(doc, patch, state, fromBackend) { - const actor = getActorId(doc) - const updated = {} - interpretPatch(patch.diffs, doc, updated) - - if (fromBackend) { - if (!patch.clock) throw new RangeError('patch is missing clock field') - if (patch.clock[actor] && patch.clock[actor] > state.seq) { - state.seq = patch.clock[actor] - } - state.clock = patch.clock - state.deps = patch.deps - state.maxOp = Math.max(state.maxOp, patch.maxOp) - } - return updateRootObject(doc, updated, state) -} - -/** - * Creates an empty document object with no changes. - */ -function init(options) { - if (typeof options === 'string') { - options = {actorId: options} - } else if (typeof options === 'undefined') { - options = {} - } else if (!isObject(options)) { - throw new TypeError(`Unsupported value for init() options: ${options}`) - } - - if (!options.deferActorId) { - if (options.actorId === undefined) { - options.actorId = uuid() - } - checkActorId(options.actorId) - } - - if (options.observable) { - const patchCallback = options.patchCallback, observable = options.observable - options.patchCallback = (patch, before, after, local, changes) => { - if (patchCallback) patchCallback(patch, before, after, local, changes) - observable.patchCallback(patch, before, after, local, changes) - } - } - - const root = {}, cache = {_root: root} - const state = {seq: 0, maxOp: 0, requests: [], clock: {}, deps: []} - if (options.backend) { - state.backendState = options.backend.init() - state.lastLocalChange = null - } - Object.defineProperty(root, OBJECT_ID, {value: '_root'}) - Object.defineProperty(root, OPTIONS, {value: Object.freeze(options)}) - Object.defineProperty(root, CONFLICTS, {value: Object.freeze({})}) - Object.defineProperty(root, CACHE, {value: Object.freeze(cache)}) - Object.defineProperty(root, STATE, {value: Object.freeze(state)}) - return Object.freeze(root) -} - -/** - * Returns a new document object initialized with the given state. - */ -function from(initialState, options) { - return change(init(options), 'Initialization', doc => Object.assign(doc, initialState)) -} - - -/** - * Changes a document `doc` according to actions taken by the local user. - * `options` is an object that can contain the following properties: - * - `message`: an optional descriptive string that is attached to the change. - * If `options` is a string, it is treated as `message`. - * - * The actual change is made within the callback function `callback`, which is - * given a mutable version of the document as argument. Returns a two-element - * array `[doc, request]` where `doc` is the updated document, and `request` - * is the change request to send to the backend. If nothing was actually - * changed, returns the original `doc` and a `null` change request. - */ -function change(doc, options, callback) { - if (doc[OBJECT_ID] !== '_root') { - throw new TypeError('The first argument to Automerge.change must be the document root') - } - if (doc[CHANGE]) { - throw new TypeError('Calls to Automerge.change cannot be nested') - } - if (typeof options === 'function' && callback === undefined) { - [options, callback] = [callback, options] - } - if (typeof options === 'string') { - options = {message: options} - } - if (options !== undefined && !isObject(options)) { - throw new TypeError('Unsupported type of options') - } - - const actorId = getActorId(doc) - if (!actorId) { - throw new Error('Actor ID must be initialized with setActorId() before making a change') - } - const context = new Context(doc, actorId) - callback(rootObjectProxy(context)) - - if (Object.keys(context.updated).length === 0) { - // If the callback didn't change anything, return the original document object unchanged - return [doc, null] - } else { - return makeChange(doc, context, options) - } -} - -/** - * Triggers a new change request on the document `doc` without actually - * modifying its data. `options` is an object as described in the documentation - * for the `change` function. This function can be useful for acknowledging the - * receipt of some message (as it's incorported into the `deps` field of the - * change). Returns a two-element array `[doc, request]` where `doc` is the - * updated document, and `request` is the change request to send to the backend. - */ -function emptyChange(doc, options) { - if (doc[OBJECT_ID] !== '_root') { - throw new TypeError('The first argument to Automerge.emptyChange must be the document root') - } - if (typeof options === 'string') { - options = {message: options} - } - if (options !== undefined && !isObject(options)) { - throw new TypeError('Unsupported type of options') - } - - const actorId = getActorId(doc) - if (!actorId) { - throw new Error('Actor ID must be initialized with setActorId() before making a change') - } - return makeChange(doc, new Context(doc, actorId), options) -} - -/** - * Applies `patch` to the document root object `doc`. This patch must come - * from the backend; it may be the result of a local change or a remote change. - * If it is the result of a local change, the `seq` field from the change - * request should be included in the patch, so that we can match them up here. - */ -function applyPatch(doc, patch, backendState = undefined) { - if (doc[OBJECT_ID] !== '_root') { - throw new TypeError('The first argument to Frontend.applyPatch must be the document root') - } - const state = copyObject(doc[STATE]) - - if (doc[OPTIONS].backend) { - if (!backendState) { - throw new RangeError('applyPatch must be called with the updated backend state') - } - state.backendState = backendState - return applyPatchToDoc(doc, patch, state, true) - } - - let baseDoc - - if (state.requests.length > 0) { - baseDoc = state.requests[0].before - if (patch.actor === getActorId(doc)) { - if (state.requests[0].seq !== patch.seq) { - throw new RangeError(`Mismatched sequence number: patch ${patch.seq} does not match next request ${state.requests[0].seq}`) - } - state.requests = state.requests.slice(1) - } else { - state.requests = state.requests.slice() - } - } else { - baseDoc = doc - state.requests = [] - } - - let newDoc = applyPatchToDoc(baseDoc, patch, state, true) - if (state.requests.length === 0) { - return newDoc - } else { - state.requests[0] = copyObject(state.requests[0]) - state.requests[0].before = newDoc - return updateRootObject(doc, {}, state) - } -} - -/** - * Returns the Automerge object ID of the given object. - */ -function getObjectId(object) { - return object[OBJECT_ID] -} - -/** - * Returns the object with the given Automerge object ID. Note: when called - * within a change callback, the returned object is read-only (not a mutable - * proxy object). - */ -function getObjectById(doc, objectId) { - // It would be nice to return a proxied object in a change callback. - // However, that requires knowing the path from the root to the current - // object, which we don't have if we jumped straight to the object by its ID. - // If we maintained an index from object ID to parent ID we could work out the path. - if (doc[CHANGE]) { - throw new TypeError('Cannot use getObjectById in a change callback') - } - return doc[CACHE][objectId] -} - -/** - * Returns the Automerge actor ID of the given document. - */ -function getActorId(doc) { - return doc[STATE].actorId || doc[OPTIONS].actorId -} - -/** - * Sets the Automerge actor ID on the document object `doc`, returning a - * document object with updated metadata. - */ -function setActorId(doc, actorId) { - checkActorId(actorId) - const state = Object.assign({}, doc[STATE], {actorId}) - return updateRootObject(doc, {}, state) -} - -/** - * Fetches the conflicts on the property `key` of `object`, which may be any - * object in a document. If `object` is a list, then `key` must be a list - * index; if `object` is a map, then `key` must be a property name. - */ -function getConflicts(object, key) { - if (object[CONFLICTS] && object[CONFLICTS][key] && - Object.keys(object[CONFLICTS][key]).length > 1) { - return object[CONFLICTS][key] - } -} - -/** - * Returns the backend state associated with the document `doc` (only used if - * a backend implementation is passed to `init()`). - */ -function getBackendState(doc, callerName = null, argPos = 'first') { - if (doc[OBJECT_ID] !== '_root') { - // Most likely cause of passing an array here is forgetting to deconstruct the return value of - // Automerge.applyChanges(). - const extraMsg = Array.isArray(doc) ? '. Note: Automerge.applyChanges now returns an array.' : '' - if (callerName) { - throw new TypeError(`The ${argPos} argument to Automerge.${callerName} must be the document root${extraMsg}`) - } else { - throw new TypeError(`Argument is not an Automerge document root${extraMsg}`) - } - } - return doc[STATE].backendState -} - -/** - * Given an array or text object from an Automerge document, returns an array - * containing the unique element ID of each list element/character. - */ -function getElementIds(list) { - if (list instanceof Text) { - return list.elems.map(elem => elem.elemId) - } else { - return list[ELEM_IDS] - } -} - -module.exports = { - init, from, change, emptyChange, applyPatch, - getObjectId, getObjectById, getActorId, setActorId, getConflicts, getLastLocalChange, - getBackendState, getElementIds, - Text, Table, Counter, Observable, Float64, Int, Uint -} - -},{"../src/common":85,"../src/uuid":86,"./apply_patch":53,"./constants":54,"./context":55,"./counter":56,"./numbers":58,"./observable":59,"./proxies":60,"./table":61,"./text":62}],58:[function(require,module,exports){ -// Convience classes to allow users to stricly specify the number type they want - -class Int { - constructor(value) { - if (!(Number.isInteger(value) && value <= Number.MAX_SAFE_INTEGER && value >= Number.MIN_SAFE_INTEGER)) { - throw new RangeError(`Value ${value} cannot be a uint`) - } - this.value = value - Object.freeze(this) - } -} - -class Uint { - constructor(value) { - if (!(Number.isInteger(value) && value <= Number.MAX_SAFE_INTEGER && value >= 0)) { - throw new RangeError(`Value ${value} cannot be a uint`) - } - this.value = value - Object.freeze(this) - } -} - -class Float64 { - constructor(value) { - if (typeof value !== 'number') { - throw new RangeError(`Value ${value} cannot be a float64`) - } - this.value = value || 0.0 - Object.freeze(this) - } -} - -module.exports = { Int, Uint, Float64 } - -},{}],59:[function(require,module,exports){ -const { OBJECT_ID, CONFLICTS } = require('./constants') - -/** - * Allows an application to register a callback when a particular object in - * a document changes. - * - * NOTE: This API is experimental and may change without warning in minor releases. - */ -class Observable { - constructor() { - this.observers = {} // map from objectId to array of observers for that object - } - - /** - * Called by an Automerge document when `patch` is applied. `before` is the - * state of the document before the patch, and `after` is the state after - * applying it. `local` is true if the update is a result of locally calling - * `Automerge.change()`, and false otherwise. `changes` is an array of - * changes that were applied to the document (as Uint8Arrays). - */ - patchCallback(patch, before, after, local, changes) { - this._objectUpdate(patch.diffs, before, after, local, changes) - } - - /** - * Recursively walks a patch and calls the callbacks for all objects that - * appear in the patch. - */ - _objectUpdate(diff, before, after, local, changes) { - if (!diff.objectId) return - if (this.observers[diff.objectId]) { - for (let callback of this.observers[diff.objectId]) { - callback(diff, before, after, local, changes) - } - } - - if (diff.type === 'map' && diff.props) { - for (const propName of Object.keys(diff.props)) { - for (const opId of Object.keys(diff.props[propName])) { - this._objectUpdate(diff.props[propName][opId], - before && before[CONFLICTS] && before[CONFLICTS][propName] && before[CONFLICTS][propName][opId], - after && after[CONFLICTS] && after[CONFLICTS][propName] && after[CONFLICTS][propName][opId], - local, changes) - } - } - - } else if (diff.type === 'table' && diff.props) { - for (const rowId of Object.keys(diff.props)) { - for (const opId of Object.keys(diff.props[rowId])) { - this._objectUpdate(diff.props[rowId][opId], - before && before.byId(rowId), - after && after.byId(rowId), - local, changes) - } - } - - } else if (diff.type === 'list' && diff.edits) { - let offset = 0 - for (const edit of diff.edits) { - if (edit.action === 'insert') { - offset -= 1 - this._objectUpdate(edit.value, undefined, - after && after[CONFLICTS] && after[CONFLICTS][edit.index] && after[CONFLICTS][edit.index][edit.elemId], - local, changes) - } else if (edit.action === 'multi-insert') { - offset -= edit.values.length - } else if (edit.action === 'update') { - this._objectUpdate(edit.value, - before && before[CONFLICTS] && before[CONFLICTS][edit.index + offset] && - before[CONFLICTS][edit.index + offset][edit.opId], - after && after[CONFLICTS] && after[CONFLICTS][edit.index] && after[CONFLICTS][edit.index][edit.opId], - local, changes) - } else if (edit.action === 'remove') { - offset += edit.count - } - } - - } else if (diff.type === 'text' && diff.edits) { - let offset = 0 - for (const edit of diff.edits) { - if (edit.action === 'insert') { - offset -= 1 - this._objectUpdate(edit.value, undefined, after && after.get(edit.index), local, changes) - } else if (edit.action === 'multi-insert') { - offset -= edit.values.length - } else if (edit.action === 'update') { - this._objectUpdate(edit.value, - before && before.get(edit.index + offset), - after && after.get(edit.index), - local, changes) - } else if (edit.action === 'remove') { - offset += edit.count - } - } - } - } - - /** - * Call this to register a callback that will get called whenever a particular - * object in a document changes. The callback is passed five arguments: the - * part of the patch describing the update to that object, the old state of - * the object, the new state of the object, a boolean that is true if the - * change is the result of calling `Automerge.change()` locally, and the array - * of binary changes applied to the document. - */ - observe(object, callback) { - const objectId = object[OBJECT_ID] - if (!objectId) throw new TypeError('The observed object must be part of an Automerge document') - if (!this.observers[objectId]) this.observers[objectId] = [] - this.observers[objectId].push(callback) - } -} - -module.exports = { Observable } - -},{"./constants":54}],60:[function(require,module,exports){ -const { OBJECT_ID, CHANGE, STATE } = require('./constants') -const { createArrayOfNulls } = require('../src/common') -const { Text } = require('./text') -const { Table } = require('./table') - -function parseListIndex(key) { - if (typeof key === 'string' && /^[0-9]+$/.test(key)) key = parseInt(key, 10) - if (typeof key !== 'number') { - throw new TypeError('A list index must be a number, but you passed ' + JSON.stringify(key)) - } - if (key < 0 || isNaN(key) || key === Infinity || key === -Infinity) { - throw new RangeError('A list index must be positive, but you passed ' + key) - } - return key -} - -function listMethods(context, listId, path) { - const methods = { - deleteAt(index, numDelete) { - context.splice(path, parseListIndex(index), numDelete || 1, []) - return this - }, - - fill(value, start, end) { - let list = context.getObject(listId) - for (let index = parseListIndex(start || 0); index < parseListIndex(end || list.length); index++) { - context.setListIndex(path, index, value) - } - return this - }, - - indexOf(o, start = 0) { - const id = o[OBJECT_ID] - if (id) { - const list = context.getObject(listId) - for (let index = start; index < list.length; index++) { - if (list[index][OBJECT_ID] === id) { - return index - } - } - return -1 - } else { - return context.getObject(listId).indexOf(o, start) - } - }, - - insertAt(index, ...values) { - context.splice(path, parseListIndex(index), 0, values) - return this - }, - - pop() { - let list = context.getObject(listId) - if (list.length == 0) return - const last = context.getObjectField(path, listId, list.length - 1) - context.splice(path, list.length - 1, 1, []) - return last - }, - - push(...values) { - let list = context.getObject(listId) - context.splice(path, list.length, 0, values) - // need to getObject() again because the list object above may be immutable - return context.getObject(listId).length - }, - - shift() { - let list = context.getObject(listId) - if (list.length == 0) return - const first = context.getObjectField(path, listId, 0) - context.splice(path, 0, 1, []) - return first - }, - - splice(start, deleteCount, ...values) { - let list = context.getObject(listId) - start = parseListIndex(start) - if (deleteCount === undefined || deleteCount > list.length - start) { - deleteCount = list.length - start - } - const deleted = [] - for (let n = 0; n < deleteCount; n++) { - deleted.push(context.getObjectField(path, listId, start + n)) - } - context.splice(path, start, deleteCount, values) - return deleted - }, - - unshift(...values) { - context.splice(path, 0, 0, values) - return context.getObject(listId).length - } - } - - for (let iterator of ['entries', 'keys', 'values']) { - let list = context.getObject(listId) - methods[iterator] = () => list[iterator]() - } - - // Read-only methods that can delegate to the JavaScript built-in implementations - for (let method of ['concat', 'every', 'filter', 'find', 'findIndex', 'forEach', 'includes', - 'join', 'lastIndexOf', 'map', 'reduce', 'reduceRight', - 'slice', 'some', 'toLocaleString', 'toString']) { - methods[method] = (...args) => { - const list = context.getObject(listId) - .map((item, index) => context.getObjectField(path, listId, index)) - return list[method](...args) - } - } - - return methods -} - -const MapHandler = { - get (target, key) { - const { context, objectId, path } = target - if (key === OBJECT_ID) return objectId - if (key === CHANGE) return context - if (key === STATE) return {actorId: context.actorId} - return context.getObjectField(path, objectId, key) - }, - - set (target, key, value) { - const { context, path, readonly } = target - if (Array.isArray(readonly) && readonly.indexOf(key) >= 0) { - throw new RangeError(`Object property "${key}" cannot be modified`) - } - context.setMapKey(path, key, value) - return true - }, - - deleteProperty (target, key) { - const { context, path, readonly } = target - if (Array.isArray(readonly) && readonly.indexOf(key) >= 0) { - throw new RangeError(`Object property "${key}" cannot be modified`) - } - context.deleteMapKey(path, key) - return true - }, - - has (target, key) { - const { context, objectId } = target - return [OBJECT_ID, CHANGE].includes(key) || (key in context.getObject(objectId)) - }, - - getOwnPropertyDescriptor (target, key) { - const { context, objectId } = target - const object = context.getObject(objectId) - if (key in object) { - return { - configurable: true, enumerable: true, - value: context.getObjectField(objectId, key) - } - } - }, - - ownKeys (target) { - const { context, objectId } = target - return Object.keys(context.getObject(objectId)) - } -} - -const ListHandler = { - get (target, key) { - const [context, objectId, path] = target - if (key === Symbol.iterator) return context.getObject(objectId)[Symbol.iterator] - if (key === OBJECT_ID) return objectId - if (key === CHANGE) return context - if (key === 'length') return context.getObject(objectId).length - if (typeof key === 'string' && /^[0-9]+$/.test(key)) { - return context.getObjectField(path, objectId, parseListIndex(key)) - } - return listMethods(context, objectId, path)[key] - }, - - set (target, key, value) { - const [context, objectId, path] = target - if (key === 'length') { - if (typeof value !== 'number') { - throw new RangeError("Invalid array length") - } - const length = context.getObject(objectId).length - if (length > value) { - context.splice(path, value, length - value, []) - } else { - context.splice(path, length, 0, createArrayOfNulls(value - length)) - } - } else { - context.setListIndex(path, parseListIndex(key), value) - } - return true - }, - - deleteProperty (target, key) { - const [context, /* objectId */, path] = target - context.splice(path, parseListIndex(key), 1, []) - return true - }, - - has (target, key) { - const [context, objectId, /* path */] = target - if (typeof key === 'string' && /^[0-9]+$/.test(key)) { - return parseListIndex(key) < context.getObject(objectId).length - } - return ['length', OBJECT_ID, CHANGE].includes(key) - }, - - getOwnPropertyDescriptor (target, key) { - const [context, objectId, /* path */] = target - const object = context.getObject(objectId) - - if (key === 'length') return {writable: true, value: object.length} - if (key === OBJECT_ID) return {configurable: false, enumerable: false, value: objectId} - - if (typeof key === 'string' && /^[0-9]+$/.test(key)) { - const index = parseListIndex(key) - if (index < object.length) return { - configurable: true, enumerable: true, - value: context.getObjectField(objectId, index) - } - } - }, - - ownKeys (target) { - const [context, objectId, /* path */] = target - const object = context.getObject(objectId) - let keys = ['length'] - for (let key of Object.keys(object)) keys.push(key) - return keys - } -} - -function mapProxy(context, objectId, path, readonly) { - return new Proxy({context, objectId, path, readonly}, MapHandler) -} - -function listProxy(context, objectId, path) { - return new Proxy([context, objectId, path], ListHandler) -} - -/** - * Instantiates a proxy object for the given `objectId`. - * This function is added as a method to the context object by rootObjectProxy(). - * When it is called, `this` is the context object. - * `readonly` is a list of map property names that cannot be modified. - */ -function instantiateProxy(path, objectId, readonly) { - const object = this.getObject(objectId) - if (Array.isArray(object)) { - return listProxy(this, objectId, path) - } else if (object instanceof Text || object instanceof Table) { - return object.getWriteable(this, path) - } else { - return mapProxy(this, objectId, path, readonly) - } -} - -function rootObjectProxy(context) { - context.instantiateObject = instantiateProxy - return mapProxy(context, '_root', []) -} - -module.exports = { rootObjectProxy } - -},{"../src/common":85,"./constants":54,"./table":61,"./text":62}],61:[function(require,module,exports){ -const { OBJECT_ID, CONFLICTS } = require('./constants') -const { isObject, copyObject } = require('../src/common') - -function compareRows(properties, row1, row2) { - for (let prop of properties) { - if (row1[prop] === row2[prop]) continue - - if (typeof row1[prop] === 'number' && typeof row2[prop] === 'number') { - return row1[prop] - row2[prop] - } else { - const prop1 = '' + row1[prop], prop2 = '' + row2[prop] - if (prop1 === prop2) continue - if (prop1 < prop2) return -1; else return +1 - } - } - return 0 -} - - -/** - * A relational-style unordered collection of records (rows). Each row is an - * object that maps column names to values. The set of rows is represented by - * a map from UUID to row object. - */ -class Table { - /** - * This constructor is used by application code when creating a new Table - * object within a change callback. - */ - constructor() { - this.entries = Object.freeze({}) - this.opIds = Object.freeze({}) - Object.freeze(this) - } - - /** - * Looks up a row in the table by its unique ID. - */ - byId(id) { - return this.entries[id] - } - - /** - * Returns an array containing the unique IDs of all rows in the table, in no - * particular order. - */ - get ids() { - return Object.keys(this.entries).filter(key => { - const entry = this.entries[key] - return isObject(entry) && entry.id === key - }) - } - - /** - * Returns the number of rows in the table. - */ - get count() { - return this.ids.length - } - - /** - * Returns an array containing all of the rows in the table, in no particular - * order. - */ - get rows() { - return this.ids.map(id => this.byId(id)) - } - - /** - * The standard JavaScript `filter()` method, which passes each row to the - * callback function and returns all rows for which the it returns true. - */ - filter(callback, thisArg) { - return this.rows.filter(callback, thisArg) - } - - /** - * The standard JavaScript `find()` method, which passes each row to the - * callback function and returns the first row for which it returns true. - */ - find(callback, thisArg) { - return this.rows.find(callback, thisArg) - } - - /** - * The standard JavaScript `map()` method, which passes each row to the - * callback function and returns a list of its return values. - */ - map(callback, thisArg) { - return this.rows.map(callback, thisArg) - } - - /** - * Returns the list of rows, sorted by one of the following: - * - If a function argument is given, it compares rows as per - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description - * - If a string argument is given, it is interpreted as a column name and - * rows are sorted according to that column. - * - If an array of strings is given, it is interpreted as a list of column - * names, and rows are sorted lexicographically by those columns. - * - If no argument is given, it sorts by row ID by default. - */ - sort(arg) { - if (typeof arg === 'function') { - return this.rows.sort(arg) - } else if (typeof arg === 'string') { - return this.rows.sort((row1, row2) => compareRows([arg], row1, row2)) - } else if (Array.isArray(arg)) { - return this.rows.sort((row1, row2) => compareRows(arg, row1, row2)) - } else if (arg === undefined) { - return this.rows.sort((row1, row2) => compareRows(['id'], row1, row2)) - } else { - throw new TypeError(`Unsupported sorting argument: ${arg}`) - } - } - - /** - * When iterating over a table, you get all rows in the table, in no - * particular order. - */ - [Symbol.iterator] () { - let rows = this.rows, index = -1 - return { - next () { - index += 1 - if (index < rows.length) { - return {done: false, value: rows[index]} - } else { - return {done: true} - } - } - } - } - - /** - * Returns a shallow clone of this object. This clone is used while applying - * a patch to the table, and `freeze()` is called on it when we have finished - * applying the patch. - */ - _clone() { - if (!this[OBJECT_ID]) { - throw new RangeError('clone() requires the objectId to be set') - } - return instantiateTable(this[OBJECT_ID], copyObject(this.entries), copyObject(this.opIds)) - } - - /** - * Sets the entry with key `id` to `value`. `opId` is the ID of the operation - * performing this assignment. This method is for internal use only; it is - * not part of the public API of Automerge.Table. - */ - _set(id, value, opId) { - if (Object.isFrozen(this.entries)) { - throw new Error('A table can only be modified in a change function') - } - if (isObject(value) && !Array.isArray(value)) { - Object.defineProperty(value, 'id', {value: id, enumerable: true}) - } - this.entries[id] = value - this.opIds[id] = opId - } - - /** - * Removes the row with unique ID `id` from the table. - */ - remove(id) { - if (Object.isFrozen(this.entries)) { - throw new Error('A table can only be modified in a change function') - } - delete this.entries[id] - delete this.opIds[id] - } - - /** - * Makes this object immutable. This is called after a change has been made. - */ - _freeze() { - Object.freeze(this.entries) - Object.freeze(this.opIds) - Object.freeze(this) - } - - /** - * Returns a writeable instance of this table. This instance is returned when - * the table is accessed within a change callback. `context` is the proxy - * context that keeps track of the mutations. - */ - getWriteable(context, path) { - if (!this[OBJECT_ID]) { - throw new RangeError('getWriteable() requires the objectId to be set') - } - - const instance = Object.create(WriteableTable.prototype) - instance[OBJECT_ID] = this[OBJECT_ID] - instance.context = context - instance.entries = this.entries - instance.opIds = this.opIds - instance.path = path - return instance - } - - /** - * Returns an object containing the table entries, indexed by objectID, - * for serializing an Automerge document to JSON. - */ - toJSON() { - const rows = {} - for (let id of this.ids) rows[id] = this.byId(id) - return rows - } -} - -/** - * An instance of this class is used when a table is accessed within a change - * callback. - */ -class WriteableTable extends Table { - /** - * Returns a proxied version of the row with ID `id`. This row object can be - * modified within a change callback. - */ - byId(id) { - if (isObject(this.entries[id]) && this.entries[id].id === id) { - const objectId = this.entries[id][OBJECT_ID] - const path = this.path.concat([{key: id, objectId}]) - return this.context.instantiateObject(path, objectId, ['id']) - } - } - - /** - * Adds a new row to the table. The row is given as a map from - * column name to value. Returns the objectId of the new row. - */ - add(row) { - return this.context.addTableRow(this.path, row) - } - - /** - * Removes the row with ID `id` from the table. Throws an exception if the row - * does not exist in the table. - */ - remove(id) { - if (isObject(this.entries[id]) && this.entries[id].id === id) { - this.context.deleteTableRow(this.path, id, this.opIds[id]) - } else { - throw new RangeError(`There is no row with ID ${id} in this table`) - } - } -} - -/** - * This function is used to instantiate a Table object in the context of - * applying a patch (see apply_patch.js). - */ -function instantiateTable(objectId, entries, opIds) { - const instance = Object.create(Table.prototype) - if (!objectId) { - throw new RangeError('instantiateTable requires an objectId to be given') - } - instance[OBJECT_ID] = objectId - instance[CONFLICTS] = Object.freeze({}) - instance.entries = entries || {} - instance.opIds = opIds || {} - return instance -} - -module.exports = { Table, instantiateTable } - -},{"../src/common":85,"./constants":54}],62:[function(require,module,exports){ -const { OBJECT_ID } = require('./constants') -const { isObject } = require('../src/common') - -class Text { - constructor (text) { - if (typeof text === 'string') { - const elems = [...text].map(value => ({value})) - return instantiateText(undefined, elems) // eslint-disable-line - } else if (Array.isArray(text)) { - const elems = text.map(value => ({value})) - return instantiateText(undefined, elems) // eslint-disable-line - } else if (text === undefined) { - return instantiateText(undefined, []) // eslint-disable-line - } else { - throw new TypeError(`Unsupported initial value for Text: ${text}`) - } - } - - get length () { - return this.elems.length - } - - get (index) { - const value = this.elems[index].value - if (this.context && isObject(value)) { - const objectId = value[OBJECT_ID] - const path = this.path.concat([{key: index, objectId}]) - return this.context.instantiateObject(path, objectId) - } else { - return value - } - } - - getElemId (index) { - return this.elems[index].elemId - } - - /** - * Iterates over the text elements character by character, including any - * inline objects. - */ - [Symbol.iterator] () { - let elems = this.elems, index = -1 - return { - next () { - index += 1 - if (index < elems.length) { - return {done: false, value: elems[index].value} - } else { - return {done: true} - } - } - } - } - - /** - * Returns the content of the Text object as a simple string, ignoring any - * non-character elements. - */ - toString() { - // Concatting to a string is faster than creating an array and then - // .join()ing for small (<100KB) arrays. - // https://jsperf.com/join-vs-loop-w-type-test - let str = '' - for (const elem of this.elems) { - if (typeof elem.value === 'string') str += elem.value - } - return str - } - - /** - * Returns the content of the Text object as a sequence of strings, - * interleaved with non-character elements. - * - * For example, the value ['a', 'b', {x: 3}, 'c', 'd'] has spans: - * => ['ab', {x: 3}, 'cd'] - */ - toSpans() { - let spans = [] - let chars = '' - for (const elem of this.elems) { - if (typeof elem.value === 'string') { - chars += elem.value - } else { - if (chars.length > 0) { - spans.push(chars) - chars = '' - } - spans.push(elem.value) - } - } - if (chars.length > 0) { - spans.push(chars) - } - return spans - } - - /** - * Returns the content of the Text object as a simple string, so that the - * JSON serialization of an Automerge document represents text nicely. - */ - toJSON() { - return this.toString() - } - - /** - * Returns a writeable instance of this object. This instance is returned when - * the text object is accessed within a change callback. `context` is the - * proxy context that keeps track of the mutations. - */ - getWriteable(context, path) { - if (!this[OBJECT_ID]) { - throw new RangeError('getWriteable() requires the objectId to be set') - } - - const instance = instantiateText(this[OBJECT_ID], this.elems) - instance.context = context - instance.path = path - return instance - } - - /** - * Updates the list item at position `index` to a new value `value`. - */ - set (index, value) { - if (this.context) { - this.context.setListIndex(this.path, index, value) - } else if (!this[OBJECT_ID]) { - this.elems[index].value = value - } else { - throw new TypeError('Automerge.Text object cannot be modified outside of a change block') - } - return this - } - - /** - * Inserts new list items `values` starting at position `index`. - */ - insertAt(index, ...values) { - if (this.context) { - this.context.splice(this.path, index, 0, values) - } else if (!this[OBJECT_ID]) { - this.elems.splice(index, 0, ...values.map(value => ({value}))) - } else { - throw new TypeError('Automerge.Text object cannot be modified outside of a change block') - } - return this - } - - /** - * Deletes `numDelete` list items starting at position `index`. - * if `numDelete` is not given, one item is deleted. - */ - deleteAt(index, numDelete = 1) { - if (this.context) { - this.context.splice(this.path, index, numDelete, []) - } else if (!this[OBJECT_ID]) { - this.elems.splice(index, numDelete) - } else { - throw new TypeError('Automerge.Text object cannot be modified outside of a change block') - } - return this - } -} - -// Read-only methods that can delegate to the JavaScript built-in array -for (let method of ['concat', 'every', 'filter', 'find', 'findIndex', 'forEach', 'includes', - 'indexOf', 'join', 'lastIndexOf', 'map', 'reduce', 'reduceRight', - 'slice', 'some', 'toLocaleString']) { - Text.prototype[method] = function (...args) { - const array = [...this] - return array[method](...args) - } -} - -function instantiateText(objectId, elems) { - const instance = Object.create(Text.prototype) - instance[OBJECT_ID] = objectId - instance.elems = elems - return instance -} - -module.exports = { Text, instantiateText } - -},{"../src/common":85,"./constants":54}],63:[function(require,module,exports){ -// Top level file is just a mixin of submodules & constants -'use strict'; - -const { Deflate, deflate, deflateRaw, gzip } = require('./lib/deflate'); - -const { Inflate, inflate, inflateRaw, ungzip } = require('./lib/inflate'); - -const constants = require('./lib/zlib/constants'); - -module.exports.Deflate = Deflate; -module.exports.deflate = deflate; -module.exports.deflateRaw = deflateRaw; -module.exports.gzip = gzip; -module.exports.Inflate = Inflate; -module.exports.inflate = inflate; -module.exports.inflateRaw = inflateRaw; -module.exports.ungzip = ungzip; -module.exports.constants = constants; - -},{"./lib/deflate":64,"./lib/inflate":65,"./lib/zlib/constants":69}],64:[function(require,module,exports){ -'use strict'; - - -const zlib_deflate = require('./zlib/deflate'); -const utils = require('./utils/common'); -const strings = require('./utils/strings'); -const msg = require('./zlib/messages'); -const ZStream = require('./zlib/zstream'); - -const toString = Object.prototype.toString; - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - -const { - Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH, - Z_OK, Z_STREAM_END, - Z_DEFAULT_COMPRESSION, - Z_DEFAULT_STRATEGY, - Z_DEFLATED -} = require('./zlib/constants'); - -/* ===========================================================================*/ - - -/** - * class Deflate - * - * Generic JS-style wrapper for zlib calls. If you don't need - * streaming behaviour - use more simple functions: [[deflate]], - * [[deflateRaw]] and [[gzip]]. - **/ - -/* internal - * Deflate.chunks -> Array - * - * Chunks of output data, if [[Deflate#onData]] not overridden. - **/ - -/** - * Deflate.result -> Uint8Array - * - * Compressed result, generated by default [[Deflate#onData]] - * and [[Deflate#onEnd]] handlers. Filled after you push last chunk - * (call [[Deflate#push]] with `Z_FINISH` / `true` param). - **/ - -/** - * Deflate.err -> Number - * - * Error code after deflate finished. 0 (Z_OK) on success. - * You will not need it in real life, because deflate errors - * are possible only on wrong options or bad `onData` / `onEnd` - * custom handlers. - **/ - -/** - * Deflate.msg -> String - * - * Error message, if [[Deflate.err]] != 0 - **/ - - -/** - * new Deflate(options) - * - options (Object): zlib deflate options. - * - * Creates new deflator instance with specified params. Throws exception - * on bad params. Supported options: - * - * - `level` - * - `windowBits` - * - `memLevel` - * - `strategy` - * - `dictionary` - * - * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) - * for more information on these. - * - * Additional options, for internal needs: - * - * - `chunkSize` - size of generated data chunks (16K by default) - * - `raw` (Boolean) - do raw deflate - * - `gzip` (Boolean) - create gzip wrapper - * - `header` (Object) - custom header for gzip - * - `text` (Boolean) - true if compressed data believed to be text - * - `time` (Number) - modification time, unix timestamp - * - `os` (Number) - operation system code - * - `extra` (Array) - array of bytes with extra data (max 65536) - * - `name` (String) - file name (binary string) - * - `comment` (String) - comment (binary string) - * - `hcrc` (Boolean) - true if header crc should be added - * - * ##### Example: - * - * ```javascript - * const pako = require('pako') - * , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) - * , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); - * - * const deflate = new pako.Deflate({ level: 3}); - * - * deflate.push(chunk1, false); - * deflate.push(chunk2, true); // true -> last chunk - * - * if (deflate.err) { throw new Error(deflate.err); } - * - * console.log(deflate.result); - * ``` - **/ -function Deflate(options) { - this.options = utils.assign({ - level: Z_DEFAULT_COMPRESSION, - method: Z_DEFLATED, - chunkSize: 16384, - windowBits: 15, - memLevel: 8, - strategy: Z_DEFAULT_STRATEGY - }, options || {}); - - let opt = this.options; - - if (opt.raw && (opt.windowBits > 0)) { - opt.windowBits = -opt.windowBits; - } - - else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) { - opt.windowBits += 16; - } - - this.err = 0; // error code, if happens (0 = Z_OK) - this.msg = ''; // error message - this.ended = false; // used to avoid multiple onEnd() calls - this.chunks = []; // chunks of compressed data - - this.strm = new ZStream(); - this.strm.avail_out = 0; - - let status = zlib_deflate.deflateInit2( - this.strm, - opt.level, - opt.method, - opt.windowBits, - opt.memLevel, - opt.strategy - ); - - if (status !== Z_OK) { - throw new Error(msg[status]); - } - - if (opt.header) { - zlib_deflate.deflateSetHeader(this.strm, opt.header); - } - - if (opt.dictionary) { - let dict; - // Convert data if needed - if (typeof opt.dictionary === 'string') { - // If we need to compress text, change encoding to utf8. - dict = strings.string2buf(opt.dictionary); - } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') { - dict = new Uint8Array(opt.dictionary); - } else { - dict = opt.dictionary; - } - - status = zlib_deflate.deflateSetDictionary(this.strm, dict); - - if (status !== Z_OK) { - throw new Error(msg[status]); - } - - this._dict_set = true; - } -} - -/** - * Deflate#push(data[, flush_mode]) -> Boolean - * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be - * converted to utf8 byte sequence. - * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. - * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH. - * - * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with - * new compressed chunks. Returns `true` on success. The last data block must - * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending - * buffers and call [[Deflate#onEnd]]. - * - * On fail call [[Deflate#onEnd]] with error code and return false. - * - * ##### Example - * - * ```javascript - * push(chunk, false); // push one of data chunks - * ... - * push(chunk, true); // push last chunk - * ``` - **/ -Deflate.prototype.push = function (data, flush_mode) { - const strm = this.strm; - const chunkSize = this.options.chunkSize; - let status, _flush_mode; - - if (this.ended) { return false; } - - if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; - else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; - - // Convert data if needed - if (typeof data === 'string') { - // If we need to compress text, change encoding to utf8. - strm.input = strings.string2buf(data); - } else if (toString.call(data) === '[object ArrayBuffer]') { - strm.input = new Uint8Array(data); - } else { - strm.input = data; - } - - strm.next_in = 0; - strm.avail_in = strm.input.length; - - for (;;) { - if (strm.avail_out === 0) { - strm.output = new Uint8Array(chunkSize); - strm.next_out = 0; - strm.avail_out = chunkSize; - } - - // Make sure avail_out > 6 to avoid repeating markers - if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) { - this.onData(strm.output.subarray(0, strm.next_out)); - strm.avail_out = 0; - continue; - } - - status = zlib_deflate.deflate(strm, _flush_mode); - - // Ended => flush and finish - if (status === Z_STREAM_END) { - if (strm.next_out > 0) { - this.onData(strm.output.subarray(0, strm.next_out)); - } - status = zlib_deflate.deflateEnd(this.strm); - this.onEnd(status); - this.ended = true; - return status === Z_OK; - } - - // Flush if out buffer full - if (strm.avail_out === 0) { - this.onData(strm.output); - continue; - } - - // Flush if requested and has data - if (_flush_mode > 0 && strm.next_out > 0) { - this.onData(strm.output.subarray(0, strm.next_out)); - strm.avail_out = 0; - continue; - } - - if (strm.avail_in === 0) break; - } - - return true; -}; - - -/** - * Deflate#onData(chunk) -> Void - * - chunk (Uint8Array): output data. - * - * By default, stores data blocks in `chunks[]` property and glue - * those in `onEnd`. Override this handler, if you need another behaviour. - **/ -Deflate.prototype.onData = function (chunk) { - this.chunks.push(chunk); -}; - - -/** - * Deflate#onEnd(status) -> Void - * - status (Number): deflate status. 0 (Z_OK) on success, - * other if not. - * - * Called once after you tell deflate that the input stream is - * complete (Z_FINISH). By default - join collected chunks, - * free memory and fill `results` / `err` properties. - **/ -Deflate.prototype.onEnd = function (status) { - // On success - join - if (status === Z_OK) { - this.result = utils.flattenChunks(this.chunks); - } - this.chunks = []; - this.err = status; - this.msg = this.strm.msg; -}; - - -/** - * deflate(data[, options]) -> Uint8Array - * - data (Uint8Array|String): input data to compress. - * - options (Object): zlib deflate options. - * - * Compress `data` with deflate algorithm and `options`. - * - * Supported options are: - * - * - level - * - windowBits - * - memLevel - * - strategy - * - dictionary - * - * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) - * for more information on these. - * - * Sugar (options): - * - * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify - * negative windowBits implicitly. - * - * ##### Example: - * - * ```javascript - * const pako = require('pako') - * const data = new Uint8Array([1,2,3,4,5,6,7,8,9]); - * - * console.log(pako.deflate(data)); - * ``` - **/ -function deflate(input, options) { - const deflator = new Deflate(options); - - deflator.push(input, true); - - // That will never happens, if you don't cheat with options :) - if (deflator.err) { throw deflator.msg || msg[deflator.err]; } - - return deflator.result; -} - - -/** - * deflateRaw(data[, options]) -> Uint8Array - * - data (Uint8Array|String): input data to compress. - * - options (Object): zlib deflate options. - * - * The same as [[deflate]], but creates raw data, without wrapper - * (header and adler32 crc). - **/ -function deflateRaw(input, options) { - options = options || {}; - options.raw = true; - return deflate(input, options); -} - - -/** - * gzip(data[, options]) -> Uint8Array - * - data (Uint8Array|String): input data to compress. - * - options (Object): zlib deflate options. - * - * The same as [[deflate]], but create gzip wrapper instead of - * deflate one. - **/ -function gzip(input, options) { - options = options || {}; - options.gzip = true; - return deflate(input, options); -} - - -module.exports.Deflate = Deflate; -module.exports.deflate = deflate; -module.exports.deflateRaw = deflateRaw; -module.exports.gzip = gzip; -module.exports.constants = require('./zlib/constants'); - -},{"./utils/common":66,"./utils/strings":67,"./zlib/constants":69,"./zlib/deflate":71,"./zlib/messages":76,"./zlib/zstream":78}],65:[function(require,module,exports){ -'use strict'; - - -const zlib_inflate = require('./zlib/inflate'); -const utils = require('./utils/common'); -const strings = require('./utils/strings'); -const msg = require('./zlib/messages'); -const ZStream = require('./zlib/zstream'); -const GZheader = require('./zlib/gzheader'); - -const toString = Object.prototype.toString; - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - -const { - Z_NO_FLUSH, Z_FINISH, - Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_STREAM_ERROR, Z_DATA_ERROR, Z_MEM_ERROR -} = require('./zlib/constants'); - -/* ===========================================================================*/ - - -/** - * class Inflate - * - * Generic JS-style wrapper for zlib calls. If you don't need - * streaming behaviour - use more simple functions: [[inflate]] - * and [[inflateRaw]]. - **/ - -/* internal - * inflate.chunks -> Array - * - * Chunks of output data, if [[Inflate#onData]] not overridden. - **/ - -/** - * Inflate.result -> Uint8Array|String - * - * Uncompressed result, generated by default [[Inflate#onData]] - * and [[Inflate#onEnd]] handlers. Filled after you push last chunk - * (call [[Inflate#push]] with `Z_FINISH` / `true` param). - **/ - -/** - * Inflate.err -> Number - * - * Error code after inflate finished. 0 (Z_OK) on success. - * Should be checked if broken data possible. - **/ - -/** - * Inflate.msg -> String - * - * Error message, if [[Inflate.err]] != 0 - **/ - - -/** - * new Inflate(options) - * - options (Object): zlib inflate options. - * - * Creates new inflator instance with specified params. Throws exception - * on bad params. Supported options: - * - * - `windowBits` - * - `dictionary` - * - * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) - * for more information on these. - * - * Additional options, for internal needs: - * - * - `chunkSize` - size of generated data chunks (16K by default) - * - `raw` (Boolean) - do raw inflate - * - `to` (String) - if equal to 'string', then result will be converted - * from utf8 to utf16 (javascript) string. When string output requested, - * chunk length can differ from `chunkSize`, depending on content. - * - * By default, when no options set, autodetect deflate/gzip data format via - * wrapper header. - * - * ##### Example: - * - * ```javascript - * const pako = require('pako') - * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) - * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); - * - * const inflate = new pako.Inflate({ level: 3}); - * - * inflate.push(chunk1, false); - * inflate.push(chunk2, true); // true -> last chunk - * - * if (inflate.err) { throw new Error(inflate.err); } - * - * console.log(inflate.result); - * ``` - **/ -function Inflate(options) { - this.options = utils.assign({ - chunkSize: 1024 * 64, - windowBits: 15, - to: '' - }, options || {}); - - const opt = this.options; - - // Force window size for `raw` data, if not set directly, - // because we have no header for autodetect. - if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) { - opt.windowBits = -opt.windowBits; - if (opt.windowBits === 0) { opt.windowBits = -15; } - } - - // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate - if ((opt.windowBits >= 0) && (opt.windowBits < 16) && - !(options && options.windowBits)) { - opt.windowBits += 32; - } - - // Gzip header has no info about windows size, we can do autodetect only - // for deflate. So, if window size not set, force it to max when gzip possible - if ((opt.windowBits > 15) && (opt.windowBits < 48)) { - // bit 3 (16) -> gzipped data - // bit 4 (32) -> autodetect gzip/deflate - if ((opt.windowBits & 15) === 0) { - opt.windowBits |= 15; - } - } - - this.err = 0; // error code, if happens (0 = Z_OK) - this.msg = ''; // error message - this.ended = false; // used to avoid multiple onEnd() calls - this.chunks = []; // chunks of compressed data - - this.strm = new ZStream(); - this.strm.avail_out = 0; - - let status = zlib_inflate.inflateInit2( - this.strm, - opt.windowBits - ); - - if (status !== Z_OK) { - throw new Error(msg[status]); - } - - this.header = new GZheader(); - - zlib_inflate.inflateGetHeader(this.strm, this.header); - - // Setup dictionary - if (opt.dictionary) { - // Convert data if needed - if (typeof opt.dictionary === 'string') { - opt.dictionary = strings.string2buf(opt.dictionary); - } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') { - opt.dictionary = new Uint8Array(opt.dictionary); - } - if (opt.raw) { //In raw mode we need to set the dictionary early - status = zlib_inflate.inflateSetDictionary(this.strm, opt.dictionary); - if (status !== Z_OK) { - throw new Error(msg[status]); - } - } - } -} - -/** - * Inflate#push(data[, flush_mode]) -> Boolean - * - data (Uint8Array|ArrayBuffer): input data - * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE - * flush modes. See constants. Skipped or `false` means Z_NO_FLUSH, - * `true` means Z_FINISH. - * - * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with - * new output chunks. Returns `true` on success. If end of stream detected, - * [[Inflate#onEnd]] will be called. - * - * `flush_mode` is not needed for normal operation, because end of stream - * detected automatically. You may try to use it for advanced things, but - * this functionality was not tested. - * - * On fail call [[Inflate#onEnd]] with error code and return false. - * - * ##### Example - * - * ```javascript - * push(chunk, false); // push one of data chunks - * ... - * push(chunk, true); // push last chunk - * ``` - **/ -Inflate.prototype.push = function (data, flush_mode) { - const strm = this.strm; - const chunkSize = this.options.chunkSize; - const dictionary = this.options.dictionary; - let status, _flush_mode, last_avail_out; - - if (this.ended) return false; - - if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; - else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; - - // Convert data if needed - if (toString.call(data) === '[object ArrayBuffer]') { - strm.input = new Uint8Array(data); - } else { - strm.input = data; - } - - strm.next_in = 0; - strm.avail_in = strm.input.length; - - for (;;) { - if (strm.avail_out === 0) { - strm.output = new Uint8Array(chunkSize); - strm.next_out = 0; - strm.avail_out = chunkSize; - } - - status = zlib_inflate.inflate(strm, _flush_mode); - - if (status === Z_NEED_DICT && dictionary) { - status = zlib_inflate.inflateSetDictionary(strm, dictionary); - - if (status === Z_OK) { - status = zlib_inflate.inflate(strm, _flush_mode); - } else if (status === Z_DATA_ERROR) { - // Replace code with more verbose - status = Z_NEED_DICT; - } - } - - // Skip snyc markers if more data follows and not raw mode - while (strm.avail_in > 0 && - status === Z_STREAM_END && - strm.state.wrap > 0 && - data[strm.next_in] !== 0) - { - zlib_inflate.inflateReset(strm); - status = zlib_inflate.inflate(strm, _flush_mode); - } - - switch (status) { - case Z_STREAM_ERROR: - case Z_DATA_ERROR: - case Z_NEED_DICT: - case Z_MEM_ERROR: - this.onEnd(status); - this.ended = true; - return false; - } - - // Remember real `avail_out` value, because we may patch out buffer content - // to align utf8 strings boundaries. - last_avail_out = strm.avail_out; - - if (strm.next_out) { - if (strm.avail_out === 0 || status === Z_STREAM_END) { - - if (this.options.to === 'string') { - - let next_out_utf8 = strings.utf8border(strm.output, strm.next_out); - - let tail = strm.next_out - next_out_utf8; - let utf8str = strings.buf2string(strm.output, next_out_utf8); - - // move tail & realign counters - strm.next_out = tail; - strm.avail_out = chunkSize - tail; - if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0); - - this.onData(utf8str); - - } else { - this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out)); - } - } - } - - // Must repeat iteration if out buffer is full - if (status === Z_OK && last_avail_out === 0) continue; - - // Finalize if end of stream reached. - if (status === Z_STREAM_END) { - status = zlib_inflate.inflateEnd(this.strm); - this.onEnd(status); - this.ended = true; - return true; - } - - if (strm.avail_in === 0) break; - } - - return true; -}; - - -/** - * Inflate#onData(chunk) -> Void - * - chunk (Uint8Array|String): output data. When string output requested, - * each chunk will be string. - * - * By default, stores data blocks in `chunks[]` property and glue - * those in `onEnd`. Override this handler, if you need another behaviour. - **/ -Inflate.prototype.onData = function (chunk) { - this.chunks.push(chunk); -}; - - -/** - * Inflate#onEnd(status) -> Void - * - status (Number): inflate status. 0 (Z_OK) on success, - * other if not. - * - * Called either after you tell inflate that the input stream is - * complete (Z_FINISH). By default - join collected chunks, - * free memory and fill `results` / `err` properties. - **/ -Inflate.prototype.onEnd = function (status) { - // On success - join - if (status === Z_OK) { - if (this.options.to === 'string') { - this.result = this.chunks.join(''); - } else { - this.result = utils.flattenChunks(this.chunks); - } - } - this.chunks = []; - this.err = status; - this.msg = this.strm.msg; -}; - - -/** - * inflate(data[, options]) -> Uint8Array|String - * - data (Uint8Array): input data to decompress. - * - options (Object): zlib inflate options. - * - * Decompress `data` with inflate/ungzip and `options`. Autodetect - * format via wrapper header by default. That's why we don't provide - * separate `ungzip` method. - * - * Supported options are: - * - * - windowBits - * - * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) - * for more information. - * - * Sugar (options): - * - * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify - * negative windowBits implicitly. - * - `to` (String) - if equal to 'string', then result will be converted - * from utf8 to utf16 (javascript) string. When string output requested, - * chunk length can differ from `chunkSize`, depending on content. - * - * - * ##### Example: - * - * ```javascript - * const pako = require('pako'); - * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9])); - * let output; - * - * try { - * output = pako.inflate(input); - * } catch (err) { - * console.log(err); - * } - * ``` - **/ -function inflate(input, options) { - const inflator = new Inflate(options); - - inflator.push(input); - - // That will never happens, if you don't cheat with options :) - if (inflator.err) throw inflator.msg || msg[inflator.err]; - - return inflator.result; -} - - -/** - * inflateRaw(data[, options]) -> Uint8Array|String - * - data (Uint8Array): input data to decompress. - * - options (Object): zlib inflate options. - * - * The same as [[inflate]], but creates raw data, without wrapper - * (header and adler32 crc). - **/ -function inflateRaw(input, options) { - options = options || {}; - options.raw = true; - return inflate(input, options); -} - - -/** - * ungzip(data[, options]) -> Uint8Array|String - * - data (Uint8Array): input data to decompress. - * - options (Object): zlib inflate options. - * - * Just shortcut to [[inflate]], because it autodetects format - * by header.content. Done for convenience. - **/ - - -module.exports.Inflate = Inflate; -module.exports.inflate = inflate; -module.exports.inflateRaw = inflateRaw; -module.exports.ungzip = inflate; -module.exports.constants = require('./zlib/constants'); - -},{"./utils/common":66,"./utils/strings":67,"./zlib/constants":69,"./zlib/gzheader":72,"./zlib/inflate":74,"./zlib/messages":76,"./zlib/zstream":78}],66:[function(require,module,exports){ -'use strict'; - - -const _has = (obj, key) => { - return Object.prototype.hasOwnProperty.call(obj, key); -}; - -module.exports.assign = function (obj /*from1, from2, from3, ...*/) { - const sources = Array.prototype.slice.call(arguments, 1); - while (sources.length) { - const source = sources.shift(); - if (!source) { continue; } - - if (typeof source !== 'object') { - throw new TypeError(source + 'must be non-object'); - } - - for (const p in source) { - if (_has(source, p)) { - obj[p] = source[p]; - } - } - } - - return obj; -}; - - -// Join array of chunks to single array. -module.exports.flattenChunks = (chunks) => { - // calculate data length - let len = 0; - - for (let i = 0, l = chunks.length; i < l; i++) { - len += chunks[i].length; - } - - // join chunks - const result = new Uint8Array(len); - - for (let i = 0, pos = 0, l = chunks.length; i < l; i++) { - let chunk = chunks[i]; - result.set(chunk, pos); - pos += chunk.length; - } - - return result; -}; - -},{}],67:[function(require,module,exports){ -// String encode/decode helpers -'use strict'; - - -// Quick check if we can use fast array to bin string conversion -// -// - apply(Array) can fail on Android 2.2 -// - apply(Uint8Array) can fail on iOS 5.1 Safari -// -let STR_APPLY_UIA_OK = true; - -try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; } - - -// Table with utf8 lengths (calculated by first byte of sequence) -// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS, -// because max possible codepoint is 0x10ffff -const _utf8len = new Uint8Array(256); -for (let q = 0; q < 256; q++) { - _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1); -} -_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start - - -// convert string to array (typed, when possible) -module.exports.string2buf = (str) => { - if (typeof TextEncoder === 'function' && TextEncoder.prototype.encode) { - return new TextEncoder().encode(str); - } - - let buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; - - // count binary size - for (m_pos = 0; m_pos < str_len; m_pos++) { - c = str.charCodeAt(m_pos); - if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { - c2 = str.charCodeAt(m_pos + 1); - if ((c2 & 0xfc00) === 0xdc00) { - c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); - m_pos++; - } - } - buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; - } - - // allocate buffer - buf = new Uint8Array(buf_len); - - // convert - for (i = 0, m_pos = 0; i < buf_len; m_pos++) { - c = str.charCodeAt(m_pos); - if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { - c2 = str.charCodeAt(m_pos + 1); - if ((c2 & 0xfc00) === 0xdc00) { - c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); - m_pos++; - } - } - if (c < 0x80) { - /* one byte */ - buf[i++] = c; - } else if (c < 0x800) { - /* two bytes */ - buf[i++] = 0xC0 | (c >>> 6); - buf[i++] = 0x80 | (c & 0x3f); - } else if (c < 0x10000) { - /* three bytes */ - buf[i++] = 0xE0 | (c >>> 12); - buf[i++] = 0x80 | (c >>> 6 & 0x3f); - buf[i++] = 0x80 | (c & 0x3f); - } else { - /* four bytes */ - buf[i++] = 0xf0 | (c >>> 18); - buf[i++] = 0x80 | (c >>> 12 & 0x3f); - buf[i++] = 0x80 | (c >>> 6 & 0x3f); - buf[i++] = 0x80 | (c & 0x3f); - } - } - - return buf; -}; - -// Helper -const buf2binstring = (buf, len) => { - // On Chrome, the arguments in a function call that are allowed is `65534`. - // If the length of the buffer is smaller than that, we can use this optimization, - // otherwise we will take a slower path. - if (len < 65534) { - if (buf.subarray && STR_APPLY_UIA_OK) { - return String.fromCharCode.apply(null, buf.length === len ? buf : buf.subarray(0, len)); - } - } - - let result = ''; - for (let i = 0; i < len; i++) { - result += String.fromCharCode(buf[i]); - } - return result; -}; - - -// convert array to string -module.exports.buf2string = (buf, max) => { - const len = max || buf.length; - - if (typeof TextDecoder === 'function' && TextDecoder.prototype.decode) { - return new TextDecoder().decode(buf.subarray(0, max)); - } - - let i, out; - - // Reserve max possible length (2 words per char) - // NB: by unknown reasons, Array is significantly faster for - // String.fromCharCode.apply than Uint16Array. - const utf16buf = new Array(len * 2); - - for (out = 0, i = 0; i < len;) { - let c = buf[i++]; - // quick process ascii - if (c < 0x80) { utf16buf[out++] = c; continue; } - - let c_len = _utf8len[c]; - // skip 5 & 6 byte codes - if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; } - - // apply mask on first byte - c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; - // join the rest - while (c_len > 1 && i < len) { - c = (c << 6) | (buf[i++] & 0x3f); - c_len--; - } - - // terminated by end of string? - if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; } - - if (c < 0x10000) { - utf16buf[out++] = c; - } else { - c -= 0x10000; - utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); - utf16buf[out++] = 0xdc00 | (c & 0x3ff); - } - } - - return buf2binstring(utf16buf, out); -}; - - -// Calculate max possible position in utf8 buffer, -// that will not break sequence. If that's not possible -// - (very small limits) return max size as is. -// -// buf[] - utf8 bytes array -// max - length limit (mandatory); -module.exports.utf8border = (buf, max) => { - - max = max || buf.length; - if (max > buf.length) { max = buf.length; } - - // go back from last position, until start of sequence found - let pos = max - 1; - while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } - - // Very small and broken sequence, - // return max, because we should return something anyway. - if (pos < 0) { return max; } - - // If we came to start of buffer - that means buffer is too small, - // return max too. - if (pos === 0) { return max; } - - return (pos + _utf8len[buf[pos]] > max) ? pos : max; -}; - -},{}],68:[function(require,module,exports){ -'use strict'; - -// Note: adler32 takes 12% for level 0 and 2% for level 6. -// It isn't worth it to make additional optimizations as in original. -// Small size is preferable. - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -const adler32 = (adler, buf, len, pos) => { - let s1 = (adler & 0xffff) |0, - s2 = ((adler >>> 16) & 0xffff) |0, - n = 0; - - while (len !== 0) { - // Set limit ~ twice less than 5552, to keep - // s2 in 31-bits, because we force signed ints. - // in other case %= will fail. - n = len > 2000 ? 2000 : len; - len -= n; - - do { - s1 = (s1 + buf[pos++]) |0; - s2 = (s2 + s1) |0; - } while (--n); - - s1 %= 65521; - s2 %= 65521; - } - - return (s1 | (s2 << 16)) |0; -}; - - -module.exports = adler32; - -},{}],69:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -module.exports = { - - /* Allowed flush values; see deflate() and inflate() below for details */ - Z_NO_FLUSH: 0, - Z_PARTIAL_FLUSH: 1, - Z_SYNC_FLUSH: 2, - Z_FULL_FLUSH: 3, - Z_FINISH: 4, - Z_BLOCK: 5, - Z_TREES: 6, - - /* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ - Z_OK: 0, - Z_STREAM_END: 1, - Z_NEED_DICT: 2, - Z_ERRNO: -1, - Z_STREAM_ERROR: -2, - Z_DATA_ERROR: -3, - Z_MEM_ERROR: -4, - Z_BUF_ERROR: -5, - //Z_VERSION_ERROR: -6, - - /* compression levels */ - Z_NO_COMPRESSION: 0, - Z_BEST_SPEED: 1, - Z_BEST_COMPRESSION: 9, - Z_DEFAULT_COMPRESSION: -1, - - - Z_FILTERED: 1, - Z_HUFFMAN_ONLY: 2, - Z_RLE: 3, - Z_FIXED: 4, - Z_DEFAULT_STRATEGY: 0, - - /* Possible values of the data_type field (though see inflate()) */ - Z_BINARY: 0, - Z_TEXT: 1, - //Z_ASCII: 1, // = Z_TEXT (deprecated) - Z_UNKNOWN: 2, - - /* The deflate compression method */ - Z_DEFLATED: 8 - //Z_NULL: null // Use -1 or null inline, depending on var type -}; - -},{}],70:[function(require,module,exports){ -'use strict'; - -// Note: we can't get significant speed boost here. -// So write code to minimize size - no pregenerated tables -// and array tools dependencies. - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -// Use ordinary array, since untyped makes no boost here -const makeTable = () => { - let c, table = []; - - for (var n = 0; n < 256; n++) { - c = n; - for (var k = 0; k < 8; k++) { - c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); - } - table[n] = c; - } - - return table; -}; - -// Create table on load. Just 255 signed longs. Not a problem. -const crcTable = new Uint32Array(makeTable()); - - -const crc32 = (crc, buf, len, pos) => { - const t = crcTable; - const end = pos + len; - - crc ^= -1; - - for (let i = pos; i < end; i++) { - crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; - } - - return (crc ^ (-1)); // >>> 0; -}; - - -module.exports = crc32; - -},{}],71:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -const { _tr_init, _tr_stored_block, _tr_flush_block, _tr_tally, _tr_align } = require('./trees'); -const adler32 = require('./adler32'); -const crc32 = require('./crc32'); -const msg = require('./messages'); - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - -const { - Z_NO_FLUSH, Z_PARTIAL_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_BLOCK, - Z_OK, Z_STREAM_END, Z_STREAM_ERROR, Z_DATA_ERROR, Z_BUF_ERROR, - Z_DEFAULT_COMPRESSION, - Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED, Z_DEFAULT_STRATEGY, - Z_UNKNOWN, - Z_DEFLATED -} = require('./constants'); - -/*============================================================================*/ - - -const MAX_MEM_LEVEL = 9; -/* Maximum value for memLevel in deflateInit2 */ -const MAX_WBITS = 15; -/* 32K LZ77 window */ -const DEF_MEM_LEVEL = 8; - - -const LENGTH_CODES = 29; -/* number of length codes, not counting the special END_BLOCK code */ -const LITERALS = 256; -/* number of literal bytes 0..255 */ -const L_CODES = LITERALS + 1 + LENGTH_CODES; -/* number of Literal or Length codes, including the END_BLOCK code */ -const D_CODES = 30; -/* number of distance codes */ -const BL_CODES = 19; -/* number of codes used to transfer the bit lengths */ -const HEAP_SIZE = 2 * L_CODES + 1; -/* maximum heap size */ -const MAX_BITS = 15; -/* All codes must not exceed MAX_BITS bits */ - -const MIN_MATCH = 3; -const MAX_MATCH = 258; -const MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); - -const PRESET_DICT = 0x20; - -const INIT_STATE = 42; -const EXTRA_STATE = 69; -const NAME_STATE = 73; -const COMMENT_STATE = 91; -const HCRC_STATE = 103; -const BUSY_STATE = 113; -const FINISH_STATE = 666; - -const BS_NEED_MORE = 1; /* block not completed, need more input or more output */ -const BS_BLOCK_DONE = 2; /* block flush performed */ -const BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ -const BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ - -const OS_CODE = 0x03; // Unix :) . Don't detect, use this default. - -const err = (strm, errorCode) => { - strm.msg = msg[errorCode]; - return errorCode; -}; - -const rank = (f) => { - return ((f) << 1) - ((f) > 4 ? 9 : 0); -}; - -const zero = (buf) => { - let len = buf.length; while (--len >= 0) { buf[len] = 0; } -}; - - -/* eslint-disable new-cap */ -let HASH_ZLIB = (s, prev, data) => ((prev << s.hash_shift) ^ data) & s.hash_mask; -// This hash causes less collisions, https://github.com/nodeca/pako/issues/135 -// But breaks binary compatibility -//let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask; -let HASH = HASH_ZLIB; - -/* ========================================================================= - * Flush as much pending output as possible. All deflate() output goes - * through this function so some applications may wish to modify it - * to avoid allocating a large strm->output buffer and copying into it. - * (See also read_buf()). - */ -const flush_pending = (strm) => { - const s = strm.state; - - //_tr_flush_bits(s); - let len = s.pending; - if (len > strm.avail_out) { - len = strm.avail_out; - } - if (len === 0) { return; } - - strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out); - strm.next_out += len; - s.pending_out += len; - strm.total_out += len; - strm.avail_out -= len; - s.pending -= len; - if (s.pending === 0) { - s.pending_out = 0; - } -}; - - -const flush_block_only = (s, last) => { - _tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); - s.block_start = s.strstart; - flush_pending(s.strm); -}; - - -const put_byte = (s, b) => { - s.pending_buf[s.pending++] = b; -}; - - -/* ========================================================================= - * Put a short in the pending buffer. The 16-bit value is put in MSB order. - * IN assertion: the stream state is correct and there is enough room in - * pending_buf. - */ -const putShortMSB = (s, b) => { - - // put_byte(s, (Byte)(b >> 8)); -// put_byte(s, (Byte)(b & 0xff)); - s.pending_buf[s.pending++] = (b >>> 8) & 0xff; - s.pending_buf[s.pending++] = b & 0xff; -}; - - -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->input buffer and copying from it. - * (See also flush_pending()). - */ -const read_buf = (strm, buf, start, size) => { - - let len = strm.avail_in; - - if (len > size) { len = size; } - if (len === 0) { return 0; } - - strm.avail_in -= len; - - // zmemcpy(buf, strm->next_in, len); - buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start); - if (strm.state.wrap === 1) { - strm.adler = adler32(strm.adler, buf, len, start); - } - - else if (strm.state.wrap === 2) { - strm.adler = crc32(strm.adler, buf, len, start); - } - - strm.next_in += len; - strm.total_in += len; - - return len; -}; - - -/* =========================================================================== - * Set match_start to the longest match starting at the given string and - * return its length. Matches shorter or equal to prev_length are discarded, - * in which case the result is equal to prev_length and match_start is - * garbage. - * IN assertions: cur_match is the head of the hash chain for the current - * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 - * OUT assertion: the match length is not greater than s->lookahead. - */ -const longest_match = (s, cur_match) => { - - let chain_length = s.max_chain_length; /* max hash chain length */ - let scan = s.strstart; /* current string */ - let match; /* matched string */ - let len; /* length of current match */ - let best_len = s.prev_length; /* best match length so far */ - let nice_match = s.nice_match; /* stop if match long enough */ - const limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? - s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; - - const _win = s.window; // shortcut - - const wmask = s.w_mask; - const prev = s.prev; - - /* Stop when cur_match becomes <= limit. To simplify the code, - * we prevent matches with the string of window index 0. - */ - - const strend = s.strstart + MAX_MATCH; - let scan_end1 = _win[scan + best_len - 1]; - let scan_end = _win[scan + best_len]; - - /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - - /* Do not waste too much time if we already have a good match: */ - if (s.prev_length >= s.good_match) { - chain_length >>= 2; - } - /* Do not look for matches beyond the end of the input. This is necessary - * to make deflate deterministic. - */ - if (nice_match > s.lookahead) { nice_match = s.lookahead; } - - // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); - - do { - // Assert(cur_match < s->strstart, "no future"); - match = cur_match; - - /* Skip to next match if the match length cannot increase - * or if the match length is less than 2. Note that the checks below - * for insufficient lookahead only occur occasionally for performance - * reasons. Therefore uninitialized memory will be accessed, and - * conditional jumps will be made that depend on those values. - * However the length of the match is limited to the lookahead, so - * the output of deflate is not affected by the uninitialized values. - */ - - if (_win[match + best_len] !== scan_end || - _win[match + best_len - 1] !== scan_end1 || - _win[match] !== _win[scan] || - _win[++match] !== _win[scan + 1]) { - continue; - } - - /* The check at best_len-1 can be removed because it will be made - * again later. (This heuristic is not always a win.) - * It is not necessary to compare scan[2] and match[2] since they - * are always equal when the other bytes match, given that - * the hash keys are equal and that HASH_BITS >= 8. - */ - scan += 2; - match++; - // Assert(*scan == *match, "match[2]?"); - - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. - */ - do { - /*jshint noempty:false*/ - } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && - scan < strend); - - // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); - - len = MAX_MATCH - (strend - scan); - scan = strend - MAX_MATCH; - - if (len > best_len) { - s.match_start = cur_match; - best_len = len; - if (len >= nice_match) { - break; - } - scan_end1 = _win[scan + best_len - 1]; - scan_end = _win[scan + best_len]; - } - } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); - - if (best_len <= s.lookahead) { - return best_len; - } - return s.lookahead; -}; - - -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -const fill_window = (s) => { - - const _w_size = s.w_size; - let p, n, m, more, str; - - //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = s.window_size - s.lookahead - s.strstart; - - // JS ints have 32 bit, block below not needed - /* Deal with !@#$% 64K limit: */ - //if (sizeof(int) <= 2) { - // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - // more = wsize; - // - // } else if (more == (unsigned)(-1)) { - // /* Very unlikely, but possible on 16 bit machine if - // * strstart == 0 && lookahead == 1 (input done a byte at time) - // */ - // more--; - // } - //} - - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { - - s.window.set(s.window.subarray(_w_size, _w_size + _w_size), 0); - s.match_start -= _w_size; - s.strstart -= _w_size; - /* we now have strstart >= MAX_DIST */ - s.block_start -= _w_size; - - /* Slide the hash table (could be avoided with 32 bit values - at the expense of memory usage). We slide even when level == 0 - to keep the hash table consistent if we switch back to level > 0 - later. (Using level 0 permanently is not an optimal usage of - zlib, so we don't care about this pathological case.) - */ - - n = s.hash_size; - p = n; - - do { - m = s.head[--p]; - s.head[p] = (m >= _w_size ? m - _w_size : 0); - } while (--n); - - n = _w_size; - p = n; - - do { - m = s.prev[--p]; - s.prev[p] = (m >= _w_size ? m - _w_size : 0); - /* If n is not on any hash chain, prev[n] is garbage but - * its value will never be used. - */ - } while (--n); - - more += _w_size; - } - if (s.strm.avail_in === 0) { - break; - } - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - //Assert(more >= 2, "more < 2"); - n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); - s.lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s.lookahead + s.insert >= MIN_MATCH) { - str = s.strstart - s.insert; - s.ins_h = s.window[str]; - - /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ - s.ins_h = HASH(s, s.ins_h, s.window[str + 1]); -//#if MIN_MATCH != 3 -// Call update_hash() MIN_MATCH-3 more times -//#endif - while (s.insert) { - /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ - s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); - - s.prev[str & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = str; - str++; - s.insert--; - if (s.lookahead + s.insert < MIN_MATCH) { - break; - } - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ -// if (s.high_water < s.window_size) { -// const curr = s.strstart + s.lookahead; -// let init = 0; -// -// if (s.high_water < curr) { -// /* Previous high water mark below current data -- zero WIN_INIT -// * bytes or up to end of window, whichever is less. -// */ -// init = s.window_size - curr; -// if (init > WIN_INIT) -// init = WIN_INIT; -// zmemzero(s->window + curr, (unsigned)init); -// s->high_water = curr + init; -// } -// else if (s->high_water < (ulg)curr + WIN_INIT) { -// /* High water mark at or above current data, but below current data -// * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up -// * to end of window, whichever is less. -// */ -// init = (ulg)curr + WIN_INIT - s->high_water; -// if (init > s->window_size - s->high_water) -// init = s->window_size - s->high_water; -// zmemzero(s->window + s->high_water, (unsigned)init); -// s->high_water += init; -// } -// } -// -// Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, -// "not enough room for search"); -}; - -/* =========================================================================== - * Copy without compression as much as possible from the input stream, return - * the current block state. - * This function does not insert new strings in the dictionary since - * uncompressible data is probably not useful. This function is used - * only for the level=0 compression option. - * NOTE: this function should be optimized to avoid extra copying from - * window to pending_buf. - */ -const deflate_stored = (s, flush) => { - - /* Stored blocks are limited to 0xffff bytes, pending_buf is limited - * to pending_buf_size, and each stored block has a 5 byte header: - */ - let max_block_size = 0xffff; - - if (max_block_size > s.pending_buf_size - 5) { - max_block_size = s.pending_buf_size - 5; - } - - /* Copy as much as possible from input to output: */ - for (;;) { - /* Fill the window as much as possible: */ - if (s.lookahead <= 1) { - - //Assert(s->strstart < s->w_size+MAX_DIST(s) || - // s->block_start >= (long)s->w_size, "slide too late"); -// if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || -// s.block_start >= s.w_size)) { -// throw new Error("slide too late"); -// } - - fill_window(s); - if (s.lookahead === 0 && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - - if (s.lookahead === 0) { - break; - } - /* flush the current block */ - } - //Assert(s->block_start >= 0L, "block gone"); -// if (s.block_start < 0) throw new Error("block gone"); - - s.strstart += s.lookahead; - s.lookahead = 0; - - /* Emit a stored block if pending_buf will be full: */ - const max_start = s.block_start + max_block_size; - - if (s.strstart === 0 || s.strstart >= max_start) { - /* strstart == 0 is possible when wraparound on 16-bit machine */ - s.lookahead = s.strstart - max_start; - s.strstart = max_start; - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - - - } - /* Flush if we may have to slide, otherwise block_start may become - * negative and the data will be gone: - */ - if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - - s.insert = 0; - - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - - if (s.strstart > s.block_start) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - return BS_NEED_MORE; -}; - -/* =========================================================================== - * Compress as much as possible from the input stream, return the current - * block state. - * This function does not perform lazy evaluation of matches and inserts - * new strings in the dictionary only for unmatched strings or for short - * matches. It is used only for the fast compression options. - */ -const deflate_fast = (s, flush) => { - - let hash_head; /* head of the hash chain */ - let bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s.lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { - break; /* flush the current block */ - } - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = 0/*NIL*/; - if (s.lookahead >= MIN_MATCH) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - - /* Find the longest match, discarding those <= prev_length. - * At this point we have always match_length < MIN_MATCH - */ - if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s.match_length = longest_match(s, hash_head); - /* longest_match() sets match_start */ - } - if (s.match_length >= MIN_MATCH) { - // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only - - /*** _tr_tally_dist(s, s.strstart - s.match_start, - s.match_length - MIN_MATCH, bflush); ***/ - bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); - - s.lookahead -= s.match_length; - - /* Insert new strings in the hash table only if the match length - * is not too large. This saves time but degrades compression. - */ - if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { - s.match_length--; /* string at strstart already in table */ - do { - s.strstart++; - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - /* strstart never exceeds WSIZE-MAX_MATCH, so there are - * always MIN_MATCH bytes ahead. - */ - } while (--s.match_length !== 0); - s.strstart++; - } else - { - s.strstart += s.match_length; - s.match_length = 0; - s.ins_h = s.window[s.strstart]; - /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]); - -//#if MIN_MATCH != 3 -// Call UPDATE_HASH() MIN_MATCH-3 more times -//#endif - /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not - * matter since it will be recomputed at next deflate call. - */ - } - } else { - /* No match, output a literal byte */ - //Tracevv((stderr,"%c", s.window[s.strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = _tr_tally(s, 0, s.window[s.strstart]); - - s.lookahead--; - s.strstart++; - } - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1); - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -}; - -/* =========================================================================== - * Same as above, but achieves better compression. We use a lazy - * evaluation for matches: a match is finally adopted only if there is - * no better match at the next window position. - */ -const deflate_slow = (s, flush) => { - - let hash_head; /* head of hash chain */ - let bflush; /* set if current block must be flushed */ - - let max_insert; - - /* Process the input block. */ - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s.lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { break; } /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = 0/*NIL*/; - if (s.lookahead >= MIN_MATCH) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - - /* Find the longest match, discarding those <= prev_length. - */ - s.prev_length = s.match_length; - s.prev_match = s.match_start; - s.match_length = MIN_MATCH - 1; - - if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && - s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s.match_length = longest_match(s, hash_head); - /* longest_match() sets match_start */ - - if (s.match_length <= 5 && - (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { - - /* If prev_match is also MIN_MATCH, match_start is garbage - * but we will ignore the current match anyway. - */ - s.match_length = MIN_MATCH - 1; - } - } - /* If there was a match at the previous step and the current - * match is not better, output the previous match: - */ - if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { - max_insert = s.strstart + s.lookahead - MIN_MATCH; - /* Do not insert strings in hash table beyond this. */ - - //check_match(s, s.strstart-1, s.prev_match, s.prev_length); - - /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, - s.prev_length - MIN_MATCH, bflush);***/ - bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH); - /* Insert in hash table all strings up to the end of the match. - * strstart-1 and strstart are already inserted. If there is not - * enough lookahead, the last two strings are not inserted in - * the hash table. - */ - s.lookahead -= s.prev_length - 1; - s.prev_length -= 2; - do { - if (++s.strstart <= max_insert) { - /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); - hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; - s.head[s.ins_h] = s.strstart; - /***/ - } - } while (--s.prev_length !== 0); - s.match_available = 0; - s.match_length = MIN_MATCH - 1; - s.strstart++; - - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - } else if (s.match_available) { - /* If there was no match at the previous position, output a - * single literal. If there was a match but the current match - * is longer, truncate the previous match to a single literal. - */ - //Tracevv((stderr,"%c", s->window[s->strstart-1])); - /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ - bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); - - if (bflush) { - /*** FLUSH_BLOCK_ONLY(s, 0) ***/ - flush_block_only(s, false); - /***/ - } - s.strstart++; - s.lookahead--; - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - } else { - /* There is no previous match to compare with, wait for - * the next step to decide. - */ - s.match_available = 1; - s.strstart++; - s.lookahead--; - } - } - //Assert (flush != Z_NO_FLUSH, "no flush?"); - if (s.match_available) { - //Tracevv((stderr,"%c", s->window[s->strstart-1])); - /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ - bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); - - s.match_available = 0; - } - s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - - return BS_BLOCK_DONE; -}; - - -/* =========================================================================== - * For Z_RLE, simply look for runs of bytes, generate matches only of distance - * one. Do not maintain a hash table. (It will be regenerated if this run of - * deflate switches away from Z_RLE.) - */ -const deflate_rle = (s, flush) => { - - let bflush; /* set if current block must be flushed */ - let prev; /* byte at distance one to match */ - let scan, strend; /* scan goes up to strend for length of run */ - - const _win = s.window; - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the longest run, plus one for the unrolled loop. - */ - if (s.lookahead <= MAX_MATCH) { - fill_window(s); - if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - if (s.lookahead === 0) { break; } /* flush the current block */ - } - - /* See how many times the previous byte repeats */ - s.match_length = 0; - if (s.lookahead >= MIN_MATCH && s.strstart > 0) { - scan = s.strstart - 1; - prev = _win[scan]; - if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { - strend = s.strstart + MAX_MATCH; - do { - /*jshint noempty:false*/ - } while (prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - prev === _win[++scan] && prev === _win[++scan] && - scan < strend); - s.match_length = MAX_MATCH - (strend - scan); - if (s.match_length > s.lookahead) { - s.match_length = s.lookahead; - } - } - //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); - } - - /* Emit match if have run of MIN_MATCH or longer, else emit literal */ - if (s.match_length >= MIN_MATCH) { - //check_match(s, s.strstart, s.strstart - 1, s.match_length); - - /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ - bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH); - - s.lookahead -= s.match_length; - s.strstart += s.match_length; - s.match_length = 0; - } else { - /* No match, output a literal byte */ - //Tracevv((stderr,"%c", s->window[s->strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = _tr_tally(s, 0, s.window[s.strstart]); - - s.lookahead--; - s.strstart++; - } - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = 0; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -}; - -/* =========================================================================== - * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. - * (It will be regenerated if this run of deflate switches away from Huffman.) - */ -const deflate_huff = (s, flush) => { - - let bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we have a literal to write. */ - if (s.lookahead === 0) { - fill_window(s); - if (s.lookahead === 0) { - if (flush === Z_NO_FLUSH) { - return BS_NEED_MORE; - } - break; /* flush the current block */ - } - } - - /* Output a literal byte */ - s.match_length = 0; - //Tracevv((stderr,"%c", s->window[s->strstart])); - /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = _tr_tally(s, 0, s.window[s.strstart]); - s.lookahead--; - s.strstart++; - if (bflush) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - } - s.insert = 0; - if (flush === Z_FINISH) { - /*** FLUSH_BLOCK(s, 1); ***/ - flush_block_only(s, true); - if (s.strm.avail_out === 0) { - return BS_FINISH_STARTED; - } - /***/ - return BS_FINISH_DONE; - } - if (s.last_lit) { - /*** FLUSH_BLOCK(s, 0); ***/ - flush_block_only(s, false); - if (s.strm.avail_out === 0) { - return BS_NEED_MORE; - } - /***/ - } - return BS_BLOCK_DONE; -}; - -/* Values for max_lazy_match, good_match and max_chain_length, depending on - * the desired pack level (0..9). The values given below have been tuned to - * exclude worst case performance for pathological files. Better values may be - * found for specific files. - */ -function Config(good_length, max_lazy, nice_length, max_chain, func) { - - this.good_length = good_length; - this.max_lazy = max_lazy; - this.nice_length = nice_length; - this.max_chain = max_chain; - this.func = func; -} - -const configuration_table = [ - /* good lazy nice chain */ - new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ - new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ - new Config(4, 5, 16, 8, deflate_fast), /* 2 */ - new Config(4, 6, 32, 32, deflate_fast), /* 3 */ - - new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ - new Config(8, 16, 32, 32, deflate_slow), /* 5 */ - new Config(8, 16, 128, 128, deflate_slow), /* 6 */ - new Config(8, 32, 128, 256, deflate_slow), /* 7 */ - new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ - new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ -]; - - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -const lm_init = (s) => { - - s.window_size = 2 * s.w_size; - - /*** CLEAR_HASH(s); ***/ - zero(s.head); // Fill with NIL (= 0); - - /* Set the default configuration parameters: - */ - s.max_lazy_match = configuration_table[s.level].max_lazy; - s.good_match = configuration_table[s.level].good_length; - s.nice_match = configuration_table[s.level].nice_length; - s.max_chain_length = configuration_table[s.level].max_chain; - - s.strstart = 0; - s.block_start = 0; - s.lookahead = 0; - s.insert = 0; - s.match_length = s.prev_length = MIN_MATCH - 1; - s.match_available = 0; - s.ins_h = 0; -}; - - -function DeflateState() { - this.strm = null; /* pointer back to this zlib stream */ - this.status = 0; /* as the name implies */ - this.pending_buf = null; /* output still pending */ - this.pending_buf_size = 0; /* size of pending_buf */ - this.pending_out = 0; /* next pending byte to output to the stream */ - this.pending = 0; /* nb of bytes in the pending buffer */ - this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ - this.gzhead = null; /* gzip header information to write */ - this.gzindex = 0; /* where in extra, name, or comment */ - this.method = Z_DEFLATED; /* can only be DEFLATED */ - this.last_flush = -1; /* value of flush param for previous deflate call */ - - this.w_size = 0; /* LZ77 window size (32K by default) */ - this.w_bits = 0; /* log2(w_size) (8..16) */ - this.w_mask = 0; /* w_size - 1 */ - - this.window = null; - /* Sliding window. Input bytes are read into the second half of the window, - * and move to the first half later to keep a dictionary of at least wSize - * bytes. With this organization, matches are limited to a distance of - * wSize-MAX_MATCH bytes, but this ensures that IO is always - * performed with a length multiple of the block size. - */ - - this.window_size = 0; - /* Actual size of window: 2*wSize, except when the user input buffer - * is directly used as sliding window. - */ - - this.prev = null; - /* Link to older string with same hash index. To limit the size of this - * array to 64K, this link is maintained only for the last 32K strings. - * An index in this array is thus a window index modulo 32K. - */ - - this.head = null; /* Heads of the hash chains or NIL. */ - - this.ins_h = 0; /* hash index of string to be inserted */ - this.hash_size = 0; /* number of elements in hash table */ - this.hash_bits = 0; /* log2(hash_size) */ - this.hash_mask = 0; /* hash_size-1 */ - - this.hash_shift = 0; - /* Number of bits by which ins_h must be shifted at each input - * step. It must be such that after MIN_MATCH steps, the oldest - * byte no longer takes part in the hash key, that is: - * hash_shift * MIN_MATCH >= hash_bits - */ - - this.block_start = 0; - /* Window position at the beginning of the current output block. Gets - * negative when the window is moved backwards. - */ - - this.match_length = 0; /* length of best match */ - this.prev_match = 0; /* previous match */ - this.match_available = 0; /* set if previous match exists */ - this.strstart = 0; /* start of string to insert */ - this.match_start = 0; /* start of matching string */ - this.lookahead = 0; /* number of valid bytes ahead in window */ - - this.prev_length = 0; - /* Length of the best match at previous step. Matches not greater than this - * are discarded. This is used in the lazy match evaluation. - */ - - this.max_chain_length = 0; - /* To speed up deflation, hash chains are never searched beyond this - * length. A higher limit improves compression ratio but degrades the - * speed. - */ - - this.max_lazy_match = 0; - /* Attempt to find a better match only when the current match is strictly - * smaller than this value. This mechanism is used only for compression - * levels >= 4. - */ - // That's alias to max_lazy_match, don't use directly - //this.max_insert_length = 0; - /* Insert new strings in the hash table only if the match length is not - * greater than this length. This saves time but degrades compression. - * max_insert_length is used only for compression levels <= 3. - */ - - this.level = 0; /* compression level (1..9) */ - this.strategy = 0; /* favor or force Huffman coding*/ - - this.good_match = 0; - /* Use a faster search when the previous match is longer than this */ - - this.nice_match = 0; /* Stop searching when current match exceeds this */ - - /* used by trees.c: */ - - /* Didn't use ct_data typedef below to suppress compiler warning */ - - // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ - // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ - // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ - - // Use flat array of DOUBLE size, with interleaved fata, - // because JS does not support effective - this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2); - this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2); - this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2); - zero(this.dyn_ltree); - zero(this.dyn_dtree); - zero(this.bl_tree); - - this.l_desc = null; /* desc. for literal tree */ - this.d_desc = null; /* desc. for distance tree */ - this.bl_desc = null; /* desc. for bit length tree */ - - //ush bl_count[MAX_BITS+1]; - this.bl_count = new Uint16Array(MAX_BITS + 1); - /* number of codes at each bit length for an optimal tree */ - - //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ - this.heap = new Uint16Array(2 * L_CODES + 1); /* heap used to build the Huffman trees */ - zero(this.heap); - - this.heap_len = 0; /* number of elements in the heap */ - this.heap_max = 0; /* element of largest frequency */ - /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - * The same heap array is used to build all trees. - */ - - this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1]; - zero(this.depth); - /* Depth of each subtree used as tie breaker for trees of equal frequency - */ - - this.l_buf = 0; /* buffer index for literals or lengths */ - - this.lit_bufsize = 0; - /* Size of match buffer for literals/lengths. There are 4 reasons for - * limiting lit_bufsize to 64K: - * - frequencies can be kept in 16 bit counters - * - if compression is not successful for the first block, all input - * data is still in the window so we can still emit a stored block even - * when input comes from standard input. (This can also be done for - * all blocks if lit_bufsize is not greater than 32K.) - * - if compression is not successful for a file smaller than 64K, we can - * even emit a stored file instead of a stored block (saving 5 bytes). - * This is applicable only for zip (not gzip or zlib). - * - creating new Huffman trees less frequently may not provide fast - * adaptation to changes in the input data statistics. (Take for - * example a binary file with poorly compressible code followed by - * a highly compressible string table.) Smaller buffer sizes give - * fast adaptation but have of course the overhead of transmitting - * trees more frequently. - * - I can't count above 4 - */ - - this.last_lit = 0; /* running index in l_buf */ - - this.d_buf = 0; - /* Buffer index for distances. To simplify the code, d_buf and l_buf have - * the same number of elements. To use different lengths, an extra flag - * array would be necessary. - */ - - this.opt_len = 0; /* bit length of current block with optimal trees */ - this.static_len = 0; /* bit length of current block with static trees */ - this.matches = 0; /* number of string matches in current block */ - this.insert = 0; /* bytes at end of window left to insert */ - - - this.bi_buf = 0; - /* Output buffer. bits are inserted starting at the bottom (least - * significant bits). - */ - this.bi_valid = 0; - /* Number of valid bits in bi_buf. All bits above the last valid bit - * are always zero. - */ - - // Used for window memory init. We safely ignore it for JS. That makes - // sense only for pointers and memory check tools. - //this.high_water = 0; - /* High water mark offset in window for initialized bytes -- bytes above - * this are set to zero in order to avoid memory check warnings when - * longest match routines access bytes past the input. This is then - * updated to the new high water mark. - */ -} - - -const deflateResetKeep = (strm) => { - - if (!strm || !strm.state) { - return err(strm, Z_STREAM_ERROR); - } - - strm.total_in = strm.total_out = 0; - strm.data_type = Z_UNKNOWN; - - const s = strm.state; - s.pending = 0; - s.pending_out = 0; - - if (s.wrap < 0) { - s.wrap = -s.wrap; - /* was made negative by deflate(..., Z_FINISH); */ - } - s.status = (s.wrap ? INIT_STATE : BUSY_STATE); - strm.adler = (s.wrap === 2) ? - 0 // crc32(0, Z_NULL, 0) - : - 1; // adler32(0, Z_NULL, 0) - s.last_flush = Z_NO_FLUSH; - _tr_init(s); - return Z_OK; -}; - - -const deflateReset = (strm) => { - - const ret = deflateResetKeep(strm); - if (ret === Z_OK) { - lm_init(strm.state); - } - return ret; -}; - - -const deflateSetHeader = (strm, head) => { - - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; } - strm.state.gzhead = head; - return Z_OK; -}; - - -const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => { - - if (!strm) { // === Z_NULL - return Z_STREAM_ERROR; - } - let wrap = 1; - - if (level === Z_DEFAULT_COMPRESSION) { - level = 6; - } - - if (windowBits < 0) { /* suppress zlib wrapper */ - wrap = 0; - windowBits = -windowBits; - } - - else if (windowBits > 15) { - wrap = 2; /* write gzip wrapper instead */ - windowBits -= 16; - } - - - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED || - windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED) { - return err(strm, Z_STREAM_ERROR); - } - - - if (windowBits === 8) { - windowBits = 9; - } - /* until 256-byte window bug fixed */ - - const s = new DeflateState(); - - strm.state = s; - s.strm = strm; - - s.wrap = wrap; - s.gzhead = null; - s.w_bits = windowBits; - s.w_size = 1 << s.w_bits; - s.w_mask = s.w_size - 1; - - s.hash_bits = memLevel + 7; - s.hash_size = 1 << s.hash_bits; - s.hash_mask = s.hash_size - 1; - s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); - - s.window = new Uint8Array(s.w_size * 2); - s.head = new Uint16Array(s.hash_size); - s.prev = new Uint16Array(s.w_size); - - // Don't need mem init magic for JS. - //s.high_water = 0; /* nothing written to s->window yet */ - - s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ - - s.pending_buf_size = s.lit_bufsize * 4; - - //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); - //s->pending_buf = (uchf *) overlay; - s.pending_buf = new Uint8Array(s.pending_buf_size); - - // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) - //s->d_buf = overlay + s->lit_bufsize/sizeof(ush); - s.d_buf = 1 * s.lit_bufsize; - - //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; - s.l_buf = (1 + 2) * s.lit_bufsize; - - s.level = level; - s.strategy = strategy; - s.method = method; - - return deflateReset(strm); -}; - -const deflateInit = (strm, level) => { - - return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); -}; - - -const deflate = (strm, flush) => { - - let beg, val; // for gzip header write only - - if (!strm || !strm.state || - flush > Z_BLOCK || flush < 0) { - return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR; - } - - const s = strm.state; - - if (!strm.output || - (!strm.input && strm.avail_in !== 0) || - (s.status === FINISH_STATE && flush !== Z_FINISH)) { - return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR); - } - - s.strm = strm; /* just in case */ - const old_flush = s.last_flush; - s.last_flush = flush; - - /* Write the header */ - if (s.status === INIT_STATE) { - - if (s.wrap === 2) { // GZIP header - strm.adler = 0; //crc32(0L, Z_NULL, 0); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (!s.gzhead) { // s->gzhead == Z_NULL - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s.level === 9 ? 2 : - (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s.status = BUSY_STATE; - } - else { - put_byte(s, (s.gzhead.text ? 1 : 0) + - (s.gzhead.hcrc ? 2 : 0) + - (!s.gzhead.extra ? 0 : 4) + - (!s.gzhead.name ? 0 : 8) + - (!s.gzhead.comment ? 0 : 16) - ); - put_byte(s, s.gzhead.time & 0xff); - put_byte(s, (s.gzhead.time >> 8) & 0xff); - put_byte(s, (s.gzhead.time >> 16) & 0xff); - put_byte(s, (s.gzhead.time >> 24) & 0xff); - put_byte(s, s.level === 9 ? 2 : - (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? - 4 : 0)); - put_byte(s, s.gzhead.os & 0xff); - if (s.gzhead.extra && s.gzhead.extra.length) { - put_byte(s, s.gzhead.extra.length & 0xff); - put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); - } - if (s.gzhead.hcrc) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0); - } - s.gzindex = 0; - s.status = EXTRA_STATE; - } - } - else // DEFLATE header - { - let header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; - let level_flags = -1; - - if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { - level_flags = 0; - } else if (s.level < 6) { - level_flags = 1; - } else if (s.level === 6) { - level_flags = 2; - } else { - level_flags = 3; - } - header |= (level_flags << 6); - if (s.strstart !== 0) { header |= PRESET_DICT; } - header += 31 - (header % 31); - - s.status = BUSY_STATE; - putShortMSB(s, header); - - /* Save the adler32 of the preset dictionary: */ - if (s.strstart !== 0) { - putShortMSB(s, strm.adler >>> 16); - putShortMSB(s, strm.adler & 0xffff); - } - strm.adler = 1; // adler32(0L, Z_NULL, 0); - } - } - -//#ifdef GZIP - if (s.status === EXTRA_STATE) { - if (s.gzhead.extra/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - - while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - break; - } - } - put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); - s.gzindex++; - } - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (s.gzindex === s.gzhead.extra.length) { - s.gzindex = 0; - s.status = NAME_STATE; - } - } - else { - s.status = NAME_STATE; - } - } - if (s.status === NAME_STATE) { - if (s.gzhead.name/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - //int val; - - do { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - val = 1; - break; - } - } - // JS specific: little magic to add zero terminator to end of string - if (s.gzindex < s.gzhead.name.length) { - val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; - } else { - val = 0; - } - put_byte(s, val); - } while (val !== 0); - - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (val === 0) { - s.gzindex = 0; - s.status = COMMENT_STATE; - } - } - else { - s.status = COMMENT_STATE; - } - } - if (s.status === COMMENT_STATE) { - if (s.gzhead.comment/* != Z_NULL*/) { - beg = s.pending; /* start of bytes to update crc */ - //int val; - - do { - if (s.pending === s.pending_buf_size) { - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - flush_pending(strm); - beg = s.pending; - if (s.pending === s.pending_buf_size) { - val = 1; - break; - } - } - // JS specific: little magic to add zero terminator to end of string - if (s.gzindex < s.gzhead.comment.length) { - val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; - } else { - val = 0; - } - put_byte(s, val); - } while (val !== 0); - - if (s.gzhead.hcrc && s.pending > beg) { - strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg); - } - if (val === 0) { - s.status = HCRC_STATE; - } - } - else { - s.status = HCRC_STATE; - } - } - if (s.status === HCRC_STATE) { - if (s.gzhead.hcrc) { - if (s.pending + 2 > s.pending_buf_size) { - flush_pending(strm); - } - if (s.pending + 2 <= s.pending_buf_size) { - put_byte(s, strm.adler & 0xff); - put_byte(s, (strm.adler >> 8) & 0xff); - strm.adler = 0; //crc32(0L, Z_NULL, 0); - s.status = BUSY_STATE; - } - } - else { - s.status = BUSY_STATE; - } - } -//#endif - - /* Flush as much pending output as possible */ - if (s.pending !== 0) { - flush_pending(strm); - if (strm.avail_out === 0) { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ - s.last_flush = -1; - return Z_OK; - } - - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Z_FINISH, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && - flush !== Z_FINISH) { - return err(strm, Z_BUF_ERROR); - } - - /* User must not provide more input after the first FINISH: */ - if (s.status === FINISH_STATE && strm.avail_in !== 0) { - return err(strm, Z_BUF_ERROR); - } - - /* Start a new block or continue the current one. - */ - if (strm.avail_in !== 0 || s.lookahead !== 0 || - (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) { - let bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : - (s.strategy === Z_RLE ? deflate_rle(s, flush) : - configuration_table[s.level].func(s, flush)); - - if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { - s.status = FINISH_STATE; - } - if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { - if (strm.avail_out === 0) { - s.last_flush = -1; - /* avoid BUF_ERROR next call, see above */ - } - return Z_OK; - /* If flush != Z_NO_FLUSH && avail_out == 0, the next call - * of deflate should use the same flush parameter to make sure - * that the flush is complete. So we don't have to output an - * empty block here, this will be done at next call. This also - * ensures that for a very small output buffer, we emit at most - * one empty block. - */ - } - if (bstate === BS_BLOCK_DONE) { - if (flush === Z_PARTIAL_FLUSH) { - _tr_align(s); - } - else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ - - _tr_stored_block(s, 0, 0, false); - /* For a full flush, this empty block will be recognized - * as a special marker by inflate_sync(). - */ - if (flush === Z_FULL_FLUSH) { - /*** CLEAR_HASH(s); ***/ /* forget history */ - zero(s.head); // Fill with NIL (= 0); - - if (s.lookahead === 0) { - s.strstart = 0; - s.block_start = 0; - s.insert = 0; - } - } - } - flush_pending(strm); - if (strm.avail_out === 0) { - s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ - return Z_OK; - } - } - } - //Assert(strm->avail_out > 0, "bug2"); - //if (strm.avail_out <= 0) { throw new Error("bug2");} - - if (flush !== Z_FINISH) { return Z_OK; } - if (s.wrap <= 0) { return Z_STREAM_END; } - - /* Write the trailer */ - if (s.wrap === 2) { - put_byte(s, strm.adler & 0xff); - put_byte(s, (strm.adler >> 8) & 0xff); - put_byte(s, (strm.adler >> 16) & 0xff); - put_byte(s, (strm.adler >> 24) & 0xff); - put_byte(s, strm.total_in & 0xff); - put_byte(s, (strm.total_in >> 8) & 0xff); - put_byte(s, (strm.total_in >> 16) & 0xff); - put_byte(s, (strm.total_in >> 24) & 0xff); - } - else - { - putShortMSB(s, strm.adler >>> 16); - putShortMSB(s, strm.adler & 0xffff); - } - - flush_pending(strm); - /* If avail_out is zero, the application will call deflate again - * to flush the rest. - */ - if (s.wrap > 0) { s.wrap = -s.wrap; } - /* write the trailer only once! */ - return s.pending !== 0 ? Z_OK : Z_STREAM_END; -}; - - -const deflateEnd = (strm) => { - - if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { - return Z_STREAM_ERROR; - } - - const status = strm.state.status; - if (status !== INIT_STATE && - status !== EXTRA_STATE && - status !== NAME_STATE && - status !== COMMENT_STATE && - status !== HCRC_STATE && - status !== BUSY_STATE && - status !== FINISH_STATE - ) { - return err(strm, Z_STREAM_ERROR); - } - - strm.state = null; - - return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK; -}; - - -/* ========================================================================= - * Initializes the compression dictionary from the given byte - * sequence without producing any compressed output. - */ -const deflateSetDictionary = (strm, dictionary) => { - - let dictLength = dictionary.length; - - if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { - return Z_STREAM_ERROR; - } - - const s = strm.state; - const wrap = s.wrap; - - if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { - return Z_STREAM_ERROR; - } - - /* when using zlib wrappers, compute Adler-32 for provided dictionary */ - if (wrap === 1) { - /* adler32(strm->adler, dictionary, dictLength); */ - strm.adler = adler32(strm.adler, dictionary, dictLength, 0); - } - - s.wrap = 0; /* avoid computing Adler-32 in read_buf */ - - /* if dictionary would fill window, just replace the history */ - if (dictLength >= s.w_size) { - if (wrap === 0) { /* already empty otherwise */ - /*** CLEAR_HASH(s); ***/ - zero(s.head); // Fill with NIL (= 0); - s.strstart = 0; - s.block_start = 0; - s.insert = 0; - } - /* use the tail */ - // dictionary = dictionary.slice(dictLength - s.w_size); - let tmpDict = new Uint8Array(s.w_size); - tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0); - dictionary = tmpDict; - dictLength = s.w_size; - } - /* insert dictionary into window and hash */ - const avail = strm.avail_in; - const next = strm.next_in; - const input = strm.input; - strm.avail_in = dictLength; - strm.next_in = 0; - strm.input = dictionary; - fill_window(s); - while (s.lookahead >= MIN_MATCH) { - let str = s.strstart; - let n = s.lookahead - (MIN_MATCH - 1); - do { - /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ - s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); - - s.prev[str & s.w_mask] = s.head[s.ins_h]; - - s.head[s.ins_h] = str; - str++; - } while (--n); - s.strstart = str; - s.lookahead = MIN_MATCH - 1; - fill_window(s); - } - s.strstart += s.lookahead; - s.block_start = s.strstart; - s.insert = s.lookahead; - s.lookahead = 0; - s.match_length = s.prev_length = MIN_MATCH - 1; - s.match_available = 0; - strm.next_in = next; - strm.input = input; - strm.avail_in = avail; - s.wrap = wrap; - return Z_OK; -}; - - -module.exports.deflateInit = deflateInit; -module.exports.deflateInit2 = deflateInit2; -module.exports.deflateReset = deflateReset; -module.exports.deflateResetKeep = deflateResetKeep; -module.exports.deflateSetHeader = deflateSetHeader; -module.exports.deflate = deflate; -module.exports.deflateEnd = deflateEnd; -module.exports.deflateSetDictionary = deflateSetDictionary; -module.exports.deflateInfo = 'pako deflate (from Nodeca project)'; - -/* Not implemented -module.exports.deflateBound = deflateBound; -module.exports.deflateCopy = deflateCopy; -module.exports.deflateParams = deflateParams; -module.exports.deflatePending = deflatePending; -module.exports.deflatePrime = deflatePrime; -module.exports.deflateTune = deflateTune; -*/ - -},{"./adler32":68,"./constants":69,"./crc32":70,"./messages":76,"./trees":77}],72:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -function GZheader() { - /* true if compressed data believed to be text */ - this.text = 0; - /* modification time */ - this.time = 0; - /* extra flags (not used when writing a gzip file) */ - this.xflags = 0; - /* operating system */ - this.os = 0; - /* pointer to extra field or Z_NULL if none */ - this.extra = null; - /* extra field length (valid if extra != Z_NULL) */ - this.extra_len = 0; // Actually, we don't need it in JS, - // but leave for few code modifications - - // - // Setup limits is not necessary because in js we should not preallocate memory - // for inflate use constant limit in 65536 bytes - // - - /* space at extra (only when reading header) */ - // this.extra_max = 0; - /* pointer to zero-terminated file name or Z_NULL */ - this.name = ''; - /* space at name (only when reading header) */ - // this.name_max = 0; - /* pointer to zero-terminated comment or Z_NULL */ - this.comment = ''; - /* space at comment (only when reading header) */ - // this.comm_max = 0; - /* true if there was or will be a header crc */ - this.hcrc = 0; - /* true when done reading gzip header (not used when writing a gzip file) */ - this.done = false; -} - -module.exports = GZheader; - -},{}],73:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -// See state defs from inflate.js -const BAD = 30; /* got a data error -- remain here until reset */ -const TYPE = 12; /* i: waiting for type bits, including last-flag bit */ - -/* - Decode literal, length, and distance codes and write out the resulting - literal and match bytes until either not enough input or output is - available, an end-of-block is encountered, or a data error is encountered. - When large enough input and output buffers are supplied to inflate(), for - example, a 16K input buffer and a 64K output buffer, more than 95% of the - inflate execution time is spent in this routine. - - Entry assumptions: - - state.mode === LEN - strm.avail_in >= 6 - strm.avail_out >= 258 - start >= strm.avail_out - state.bits < 8 - - On return, state.mode is one of: - - LEN -- ran out of enough output space or enough available input - TYPE -- reached end of block code, inflate() to interpret next block - BAD -- error in block data - - Notes: - - - The maximum input bits used by a length/distance pair is 15 bits for the - length code, 5 bits for the length extra, 15 bits for the distance code, - and 13 bits for the distance extra. This totals 48 bits, or six bytes. - Therefore if strm.avail_in >= 6, then there is enough input to avoid - checking for available input while decoding. - - - The maximum bytes that a single length/distance pair can output is 258 - bytes, which is the maximum length that can be coded. inflate_fast() - requires strm.avail_out >= 258 for each loop to avoid checking for - output space. - */ -module.exports = function inflate_fast(strm, start) { - let _in; /* local strm.input */ - let last; /* have enough input while in < last */ - let _out; /* local strm.output */ - let beg; /* inflate()'s initial strm.output */ - let end; /* while out < end, enough space available */ -//#ifdef INFLATE_STRICT - let dmax; /* maximum distance from zlib header */ -//#endif - let wsize; /* window size or zero if not using window */ - let whave; /* valid bytes in the window */ - let wnext; /* window write index */ - // Use `s_window` instead `window`, avoid conflict with instrumentation tools - let s_window; /* allocated sliding window, if wsize != 0 */ - let hold; /* local strm.hold */ - let bits; /* local strm.bits */ - let lcode; /* local strm.lencode */ - let dcode; /* local strm.distcode */ - let lmask; /* mask for first level of length codes */ - let dmask; /* mask for first level of distance codes */ - let here; /* retrieved table entry */ - let op; /* code bits, operation, extra bits, or */ - /* window position, window bytes to copy */ - let len; /* match length, unused bytes */ - let dist; /* match distance */ - let from; /* where to copy match from */ - let from_source; - - - let input, output; // JS specific, because we have no pointers - - /* copy state to local variables */ - const state = strm.state; - //here = state.here; - _in = strm.next_in; - input = strm.input; - last = _in + (strm.avail_in - 5); - _out = strm.next_out; - output = strm.output; - beg = _out - (start - strm.avail_out); - end = _out + (strm.avail_out - 257); -//#ifdef INFLATE_STRICT - dmax = state.dmax; -//#endif - wsize = state.wsize; - whave = state.whave; - wnext = state.wnext; - s_window = state.window; - hold = state.hold; - bits = state.bits; - lcode = state.lencode; - dcode = state.distcode; - lmask = (1 << state.lenbits) - 1; - dmask = (1 << state.distbits) - 1; - - - /* decode literals and length/distances until end-of-block or not enough - input data or output space */ - - top: - do { - if (bits < 15) { - hold += input[_in++] << bits; - bits += 8; - hold += input[_in++] << bits; - bits += 8; - } - - here = lcode[hold & lmask]; - - dolen: - for (;;) { // Goto emulation - op = here >>> 24/*here.bits*/; - hold >>>= op; - bits -= op; - op = (here >>> 16) & 0xff/*here.op*/; - if (op === 0) { /* literal */ - //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - // "inflate: literal '%c'\n" : - // "inflate: literal 0x%02x\n", here.val)); - output[_out++] = here & 0xffff/*here.val*/; - } - else if (op & 16) { /* length base */ - len = here & 0xffff/*here.val*/; - op &= 15; /* number of extra bits */ - if (op) { - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - } - len += hold & ((1 << op) - 1); - hold >>>= op; - bits -= op; - } - //Tracevv((stderr, "inflate: length %u\n", len)); - if (bits < 15) { - hold += input[_in++] << bits; - bits += 8; - hold += input[_in++] << bits; - bits += 8; - } - here = dcode[hold & dmask]; - - dodist: - for (;;) { // goto emulation - op = here >>> 24/*here.bits*/; - hold >>>= op; - bits -= op; - op = (here >>> 16) & 0xff/*here.op*/; - - if (op & 16) { /* distance base */ - dist = here & 0xffff/*here.val*/; - op &= 15; /* number of extra bits */ - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - if (bits < op) { - hold += input[_in++] << bits; - bits += 8; - } - } - dist += hold & ((1 << op) - 1); -//#ifdef INFLATE_STRICT - if (dist > dmax) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break top; - } -//#endif - hold >>>= op; - bits -= op; - //Tracevv((stderr, "inflate: distance %u\n", dist)); - op = _out - beg; /* max distance in output */ - if (dist > op) { /* see if copy from window */ - op = dist - op; /* distance back in window */ - if (op > whave) { - if (state.sane) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break top; - } - -// (!) This block is disabled in zlib defaults, -// don't enable it for binary compatibility -//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR -// if (len <= op - whave) { -// do { -// output[_out++] = 0; -// } while (--len); -// continue top; -// } -// len -= op - whave; -// do { -// output[_out++] = 0; -// } while (--op > whave); -// if (op === 0) { -// from = _out - dist; -// do { -// output[_out++] = output[from++]; -// } while (--len); -// continue top; -// } -//#endif - } - from = 0; // window index - from_source = s_window; - if (wnext === 0) { /* very common case */ - from += wsize - op; - if (op < len) { /* some from window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - else if (wnext < op) { /* wrap around window */ - from += wsize + wnext - op; - op -= wnext; - if (op < len) { /* some from end of window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = 0; - if (wnext < len) { /* some from start of window */ - op = wnext; - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - } - else { /* contiguous in window */ - from += wnext - op; - if (op < len) { /* some from window */ - len -= op; - do { - output[_out++] = s_window[from++]; - } while (--op); - from = _out - dist; /* rest from output */ - from_source = output; - } - } - while (len > 2) { - output[_out++] = from_source[from++]; - output[_out++] = from_source[from++]; - output[_out++] = from_source[from++]; - len -= 3; - } - if (len) { - output[_out++] = from_source[from++]; - if (len > 1) { - output[_out++] = from_source[from++]; - } - } - } - else { - from = _out - dist; /* copy direct from output */ - do { /* minimum length is three */ - output[_out++] = output[from++]; - output[_out++] = output[from++]; - output[_out++] = output[from++]; - len -= 3; - } while (len > 2); - if (len) { - output[_out++] = output[from++]; - if (len > 1) { - output[_out++] = output[from++]; - } - } - } - } - else if ((op & 64) === 0) { /* 2nd level distance code */ - here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; - continue dodist; - } - else { - strm.msg = 'invalid distance code'; - state.mode = BAD; - break top; - } - - break; // need to emulate goto via "continue" - } - } - else if ((op & 64) === 0) { /* 2nd level length code */ - here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; - continue dolen; - } - else if (op & 32) { /* end-of-block */ - //Tracevv((stderr, "inflate: end of block\n")); - state.mode = TYPE; - break top; - } - else { - strm.msg = 'invalid literal/length code'; - state.mode = BAD; - break top; - } - - break; // need to emulate goto via "continue" - } - } while (_in < last && _out < end); - - /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ - len = bits >> 3; - _in -= len; - bits -= len << 3; - hold &= (1 << bits) - 1; - - /* update state and return */ - strm.next_in = _in; - strm.next_out = _out; - strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); - strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); - state.hold = hold; - state.bits = bits; - return; -}; - -},{}],74:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -const adler32 = require('./adler32'); -const crc32 = require('./crc32'); -const inflate_fast = require('./inffast'); -const inflate_table = require('./inftrees'); - -const CODES = 0; -const LENS = 1; -const DISTS = 2; - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - -const { - Z_FINISH, Z_BLOCK, Z_TREES, - Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_STREAM_ERROR, Z_DATA_ERROR, Z_MEM_ERROR, Z_BUF_ERROR, - Z_DEFLATED -} = require('./constants'); - - -/* STATES ====================================================================*/ -/* ===========================================================================*/ - - -const HEAD = 1; /* i: waiting for magic header */ -const FLAGS = 2; /* i: waiting for method and flags (gzip) */ -const TIME = 3; /* i: waiting for modification time (gzip) */ -const OS = 4; /* i: waiting for extra flags and operating system (gzip) */ -const EXLEN = 5; /* i: waiting for extra length (gzip) */ -const EXTRA = 6; /* i: waiting for extra bytes (gzip) */ -const NAME = 7; /* i: waiting for end of file name (gzip) */ -const COMMENT = 8; /* i: waiting for end of comment (gzip) */ -const HCRC = 9; /* i: waiting for header crc (gzip) */ -const DICTID = 10; /* i: waiting for dictionary check value */ -const DICT = 11; /* waiting for inflateSetDictionary() call */ -const TYPE = 12; /* i: waiting for type bits, including last-flag bit */ -const TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ -const STORED = 14; /* i: waiting for stored size (length and complement) */ -const COPY_ = 15; /* i/o: same as COPY below, but only first time in */ -const COPY = 16; /* i/o: waiting for input or output to copy stored block */ -const TABLE = 17; /* i: waiting for dynamic block table lengths */ -const LENLENS = 18; /* i: waiting for code length code lengths */ -const CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ -const LEN_ = 20; /* i: same as LEN below, but only first time in */ -const LEN = 21; /* i: waiting for length/lit/eob code */ -const LENEXT = 22; /* i: waiting for length extra bits */ -const DIST = 23; /* i: waiting for distance code */ -const DISTEXT = 24; /* i: waiting for distance extra bits */ -const MATCH = 25; /* o: waiting for output space to copy string */ -const LIT = 26; /* o: waiting for output space to write literal */ -const CHECK = 27; /* i: waiting for 32-bit check value */ -const LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ -const DONE = 29; /* finished check, done -- remain here until reset */ -const BAD = 30; /* got a data error -- remain here until reset */ -const MEM = 31; /* got an inflate() memory error -- remain here until reset */ -const SYNC = 32; /* looking for synchronization bytes to restart inflate() */ - -/* ===========================================================================*/ - - - -const ENOUGH_LENS = 852; -const ENOUGH_DISTS = 592; -//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); - -const MAX_WBITS = 15; -/* 32K LZ77 window */ -const DEF_WBITS = MAX_WBITS; - - -const zswap32 = (q) => { - - return (((q >>> 24) & 0xff) + - ((q >>> 8) & 0xff00) + - ((q & 0xff00) << 8) + - ((q & 0xff) << 24)); -}; - - -function InflateState() { - this.mode = 0; /* current inflate mode */ - this.last = false; /* true if processing last block */ - this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ - this.havedict = false; /* true if dictionary provided */ - this.flags = 0; /* gzip header method and flags (0 if zlib) */ - this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ - this.check = 0; /* protected copy of check value */ - this.total = 0; /* protected copy of output count */ - // TODO: may be {} - this.head = null; /* where to save gzip header information */ - - /* sliding window */ - this.wbits = 0; /* log base 2 of requested window size */ - this.wsize = 0; /* window size or zero if not using window */ - this.whave = 0; /* valid bytes in the window */ - this.wnext = 0; /* window write index */ - this.window = null; /* allocated sliding window, if needed */ - - /* bit accumulator */ - this.hold = 0; /* input bit accumulator */ - this.bits = 0; /* number of bits in "in" */ - - /* for string and stored block copying */ - this.length = 0; /* literal or length of data to copy */ - this.offset = 0; /* distance back to copy string from */ - - /* for table and code decoding */ - this.extra = 0; /* extra bits needed */ - - /* fixed and dynamic code tables */ - this.lencode = null; /* starting table for length/literal codes */ - this.distcode = null; /* starting table for distance codes */ - this.lenbits = 0; /* index bits for lencode */ - this.distbits = 0; /* index bits for distcode */ - - /* dynamic table building */ - this.ncode = 0; /* number of code length code lengths */ - this.nlen = 0; /* number of length code lengths */ - this.ndist = 0; /* number of distance code lengths */ - this.have = 0; /* number of code lengths in lens[] */ - this.next = null; /* next available space in codes[] */ - - this.lens = new Uint16Array(320); /* temporary storage for code lengths */ - this.work = new Uint16Array(288); /* work area for code table building */ - - /* - because we don't have pointers in js, we use lencode and distcode directly - as buffers so we don't need codes - */ - //this.codes = new Int32Array(ENOUGH); /* space for code tables */ - this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ - this.distdyn = null; /* dynamic table for distance codes (JS specific) */ - this.sane = 0; /* if false, allow invalid distance too far */ - this.back = 0; /* bits back of last unprocessed length/lit */ - this.was = 0; /* initial length of match */ -} - - -const inflateResetKeep = (strm) => { - - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - const state = strm.state; - strm.total_in = strm.total_out = state.total = 0; - strm.msg = ''; /*Z_NULL*/ - if (state.wrap) { /* to support ill-conceived Java test suite */ - strm.adler = state.wrap & 1; - } - state.mode = HEAD; - state.last = 0; - state.havedict = 0; - state.dmax = 32768; - state.head = null/*Z_NULL*/; - state.hold = 0; - state.bits = 0; - //state.lencode = state.distcode = state.next = state.codes; - state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS); - state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS); - - state.sane = 1; - state.back = -1; - //Tracev((stderr, "inflate: reset\n")); - return Z_OK; -}; - - -const inflateReset = (strm) => { - - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - const state = strm.state; - state.wsize = 0; - state.whave = 0; - state.wnext = 0; - return inflateResetKeep(strm); - -}; - - -const inflateReset2 = (strm, windowBits) => { - let wrap; - - /* get the state */ - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - const state = strm.state; - - /* extract wrap request from windowBits parameter */ - if (windowBits < 0) { - wrap = 0; - windowBits = -windowBits; - } - else { - wrap = (windowBits >> 4) + 1; - if (windowBits < 48) { - windowBits &= 15; - } - } - - /* set number of window bits, free window if different */ - if (windowBits && (windowBits < 8 || windowBits > 15)) { - return Z_STREAM_ERROR; - } - if (state.window !== null && state.wbits !== windowBits) { - state.window = null; - } - - /* update state and reset the rest of it */ - state.wrap = wrap; - state.wbits = windowBits; - return inflateReset(strm); -}; - - -const inflateInit2 = (strm, windowBits) => { - - if (!strm) { return Z_STREAM_ERROR; } - //strm.msg = Z_NULL; /* in case we return an error */ - - const state = new InflateState(); - - //if (state === Z_NULL) return Z_MEM_ERROR; - //Tracev((stderr, "inflate: allocated\n")); - strm.state = state; - state.window = null/*Z_NULL*/; - const ret = inflateReset2(strm, windowBits); - if (ret !== Z_OK) { - strm.state = null/*Z_NULL*/; - } - return ret; -}; - - -const inflateInit = (strm) => { - - return inflateInit2(strm, DEF_WBITS); -}; - - -/* - Return state with length and distance decoding tables and index sizes set to - fixed code decoding. Normally this returns fixed tables from inffixed.h. - If BUILDFIXED is defined, then instead this routine builds the tables the - first time it's called, and returns those tables the first time and - thereafter. This reduces the size of the code by about 2K bytes, in - exchange for a little execution time. However, BUILDFIXED should not be - used for threaded applications, since the rewriting of the tables and virgin - may not be thread-safe. - */ -let virgin = true; - -let lenfix, distfix; // We have no pointers in JS, so keep tables separate - - -const fixedtables = (state) => { - - /* build fixed huffman tables if first call (may not be thread safe) */ - if (virgin) { - lenfix = new Int32Array(512); - distfix = new Int32Array(32); - - /* literal/length table */ - let sym = 0; - while (sym < 144) { state.lens[sym++] = 8; } - while (sym < 256) { state.lens[sym++] = 9; } - while (sym < 280) { state.lens[sym++] = 7; } - while (sym < 288) { state.lens[sym++] = 8; } - - inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); - - /* distance table */ - sym = 0; - while (sym < 32) { state.lens[sym++] = 5; } - - inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); - - /* do this just once */ - virgin = false; - } - - state.lencode = lenfix; - state.lenbits = 9; - state.distcode = distfix; - state.distbits = 5; -}; - - -/* - Update the window with the last wsize (normally 32K) bytes written before - returning. If window does not exist yet, create it. This is only called - when a window is already in use, or when output has been written during this - inflate call, but the end of the deflate stream has not been reached yet. - It is also called to create a window for dictionary data when a dictionary - is loaded. - - Providing output buffers larger than 32K to inflate() should provide a speed - advantage, since only the last 32K of output is copied to the sliding window - upon return from inflate(), and since all distances after the first 32K of - output will fall in the output data, making match copies simpler and faster. - The advantage may be dependent on the size of the processor's data caches. - */ -const updatewindow = (strm, src, end, copy) => { - - let dist; - const state = strm.state; - - /* if it hasn't been done already, allocate space for the window */ - if (state.window === null) { - state.wsize = 1 << state.wbits; - state.wnext = 0; - state.whave = 0; - - state.window = new Uint8Array(state.wsize); - } - - /* copy state->wsize or less output bytes into the circular window */ - if (copy >= state.wsize) { - state.window.set(src.subarray(end - state.wsize, end), 0); - state.wnext = 0; - state.whave = state.wsize; - } - else { - dist = state.wsize - state.wnext; - if (dist > copy) { - dist = copy; - } - //zmemcpy(state->window + state->wnext, end - copy, dist); - state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext); - copy -= dist; - if (copy) { - //zmemcpy(state->window, end - copy, copy); - state.window.set(src.subarray(end - copy, end), 0); - state.wnext = copy; - state.whave = state.wsize; - } - else { - state.wnext += dist; - if (state.wnext === state.wsize) { state.wnext = 0; } - if (state.whave < state.wsize) { state.whave += dist; } - } - } - return 0; -}; - - -const inflate = (strm, flush) => { - - let state; - let input, output; // input/output buffers - let next; /* next input INDEX */ - let put; /* next output INDEX */ - let have, left; /* available input and output */ - let hold; /* bit buffer */ - let bits; /* bits in bit buffer */ - let _in, _out; /* save starting available input and output */ - let copy; /* number of stored or match bytes to copy */ - let from; /* where to copy match bytes from */ - let from_source; - let here = 0; /* current decoding table entry */ - let here_bits, here_op, here_val; // paked "here" denormalized (JS specific) - //let last; /* parent table entry */ - let last_bits, last_op, last_val; // paked "last" denormalized (JS specific) - let len; /* length to copy for repeats, bits to drop */ - let ret; /* return code */ - const hbuf = new Uint8Array(4); /* buffer for gzip header crc calculation */ - let opts; - - let n; // temporary variable for NEED_BITS - - const order = /* permutation of code lengths */ - new Uint8Array([ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]); - - - if (!strm || !strm.state || !strm.output || - (!strm.input && strm.avail_in !== 0)) { - return Z_STREAM_ERROR; - } - - state = strm.state; - if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ - - - //--- LOAD() --- - put = strm.next_out; - output = strm.output; - left = strm.avail_out; - next = strm.next_in; - input = strm.input; - have = strm.avail_in; - hold = state.hold; - bits = state.bits; - //--- - - _in = have; - _out = left; - ret = Z_OK; - - inf_leave: // goto emulation - for (;;) { - switch (state.mode) { - case HEAD: - if (state.wrap === 0) { - state.mode = TYPEDO; - break; - } - //=== NEEDBITS(16); - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ - state.check = 0/*crc32(0L, Z_NULL, 0)*/; - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = FLAGS; - break; - } - state.flags = 0; /* expect zlib header */ - if (state.head) { - state.head.done = false; - } - if (!(state.wrap & 1) || /* check if zlib header allowed */ - (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { - strm.msg = 'incorrect header check'; - state.mode = BAD; - break; - } - if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { - strm.msg = 'unknown compression method'; - state.mode = BAD; - break; - } - //--- DROPBITS(4) ---// - hold >>>= 4; - bits -= 4; - //---// - len = (hold & 0x0f)/*BITS(4)*/ + 8; - if (state.wbits === 0) { - state.wbits = len; - } - else if (len > state.wbits) { - strm.msg = 'invalid window size'; - state.mode = BAD; - break; - } - - // !!! pako patch. Force use `options.windowBits` if passed. - // Required to always use max window size by default. - state.dmax = 1 << state.wbits; - //state.dmax = 1 << len; - - //Tracev((stderr, "inflate: zlib header ok\n")); - strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; - state.mode = hold & 0x200 ? DICTID : TYPE; - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - break; - case FLAGS: - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.flags = hold; - if ((state.flags & 0xff) !== Z_DEFLATED) { - strm.msg = 'unknown compression method'; - state.mode = BAD; - break; - } - if (state.flags & 0xe000) { - strm.msg = 'unknown header flags set'; - state.mode = BAD; - break; - } - if (state.head) { - state.head.text = ((hold >> 8) & 1); - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = TIME; - /* falls through */ - case TIME: - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (state.head) { - state.head.time = hold; - } - if (state.flags & 0x0200) { - //=== CRC4(state.check, hold) - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - hbuf[2] = (hold >>> 16) & 0xff; - hbuf[3] = (hold >>> 24) & 0xff; - state.check = crc32(state.check, hbuf, 4, 0); - //=== - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = OS; - /* falls through */ - case OS: - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (state.head) { - state.head.xflags = (hold & 0xff); - state.head.os = (hold >> 8); - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = EXLEN; - /* falls through */ - case EXLEN: - if (state.flags & 0x0400) { - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.length = hold; - if (state.head) { - state.head.extra_len = hold; - } - if (state.flags & 0x0200) { - //=== CRC2(state.check, hold); - hbuf[0] = hold & 0xff; - hbuf[1] = (hold >>> 8) & 0xff; - state.check = crc32(state.check, hbuf, 2, 0); - //===// - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - } - else if (state.head) { - state.head.extra = null/*Z_NULL*/; - } - state.mode = EXTRA; - /* falls through */ - case EXTRA: - if (state.flags & 0x0400) { - copy = state.length; - if (copy > have) { copy = have; } - if (copy) { - if (state.head) { - len = state.head.extra_len - state.length; - if (!state.head.extra) { - // Use untyped array for more convenient processing later - state.head.extra = new Uint8Array(state.head.extra_len); - } - state.head.extra.set( - input.subarray( - next, - // extra field is limited to 65536 bytes - // - no need for additional size check - next + copy - ), - /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ - len - ); - //zmemcpy(state.head.extra + len, next, - // len + copy > state.head.extra_max ? - // state.head.extra_max - len : copy); - } - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - state.length -= copy; - } - if (state.length) { break inf_leave; } - } - state.length = 0; - state.mode = NAME; - /* falls through */ - case NAME: - if (state.flags & 0x0800) { - if (have === 0) { break inf_leave; } - copy = 0; - do { - // TODO: 2 or 1 bytes? - len = input[next + copy++]; - /* use constant limit because in js we should not preallocate memory */ - if (state.head && len && - (state.length < 65536 /*state.head.name_max*/)) { - state.head.name += String.fromCharCode(len); - } - } while (len && copy < have); - - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - if (len) { break inf_leave; } - } - else if (state.head) { - state.head.name = null; - } - state.length = 0; - state.mode = COMMENT; - /* falls through */ - case COMMENT: - if (state.flags & 0x1000) { - if (have === 0) { break inf_leave; } - copy = 0; - do { - len = input[next + copy++]; - /* use constant limit because in js we should not preallocate memory */ - if (state.head && len && - (state.length < 65536 /*state.head.comm_max*/)) { - state.head.comment += String.fromCharCode(len); - } - } while (len && copy < have); - if (state.flags & 0x0200) { - state.check = crc32(state.check, input, copy, next); - } - have -= copy; - next += copy; - if (len) { break inf_leave; } - } - else if (state.head) { - state.head.comment = null; - } - state.mode = HCRC; - /* falls through */ - case HCRC: - if (state.flags & 0x0200) { - //=== NEEDBITS(16); */ - while (bits < 16) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (hold !== (state.check & 0xffff)) { - strm.msg = 'header crc mismatch'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - } - if (state.head) { - state.head.hcrc = ((state.flags >> 9) & 1); - state.head.done = true; - } - strm.adler = state.check = 0; - state.mode = TYPE; - break; - case DICTID: - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - strm.adler = state.check = zswap32(hold); - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = DICT; - /* falls through */ - case DICT: - if (state.havedict === 0) { - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - return Z_NEED_DICT; - } - strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; - state.mode = TYPE; - /* falls through */ - case TYPE: - if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } - /* falls through */ - case TYPEDO: - if (state.last) { - //--- BYTEBITS() ---// - hold >>>= bits & 7; - bits -= bits & 7; - //---// - state.mode = CHECK; - break; - } - //=== NEEDBITS(3); */ - while (bits < 3) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.last = (hold & 0x01)/*BITS(1)*/; - //--- DROPBITS(1) ---// - hold >>>= 1; - bits -= 1; - //---// - - switch ((hold & 0x03)/*BITS(2)*/) { - case 0: /* stored block */ - //Tracev((stderr, "inflate: stored block%s\n", - // state.last ? " (last)" : "")); - state.mode = STORED; - break; - case 1: /* fixed block */ - fixedtables(state); - //Tracev((stderr, "inflate: fixed codes block%s\n", - // state.last ? " (last)" : "")); - state.mode = LEN_; /* decode codes */ - if (flush === Z_TREES) { - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - break inf_leave; - } - break; - case 2: /* dynamic block */ - //Tracev((stderr, "inflate: dynamic codes block%s\n", - // state.last ? " (last)" : "")); - state.mode = TABLE; - break; - case 3: - strm.msg = 'invalid block type'; - state.mode = BAD; - } - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - break; - case STORED: - //--- BYTEBITS() ---// /* go to byte boundary */ - hold >>>= bits & 7; - bits -= bits & 7; - //---// - //=== NEEDBITS(32); */ - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { - strm.msg = 'invalid stored block lengths'; - state.mode = BAD; - break; - } - state.length = hold & 0xffff; - //Tracev((stderr, "inflate: stored length %u\n", - // state.length)); - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - state.mode = COPY_; - if (flush === Z_TREES) { break inf_leave; } - /* falls through */ - case COPY_: - state.mode = COPY; - /* falls through */ - case COPY: - copy = state.length; - if (copy) { - if (copy > have) { copy = have; } - if (copy > left) { copy = left; } - if (copy === 0) { break inf_leave; } - //--- zmemcpy(put, next, copy); --- - output.set(input.subarray(next, next + copy), put); - //---// - have -= copy; - next += copy; - left -= copy; - put += copy; - state.length -= copy; - break; - } - //Tracev((stderr, "inflate: stored end\n")); - state.mode = TYPE; - break; - case TABLE: - //=== NEEDBITS(14); */ - while (bits < 14) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; - //--- DROPBITS(5) ---// - hold >>>= 5; - bits -= 5; - //---// - state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; - //--- DROPBITS(5) ---// - hold >>>= 5; - bits -= 5; - //---// - state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; - //--- DROPBITS(4) ---// - hold >>>= 4; - bits -= 4; - //---// -//#ifndef PKZIP_BUG_WORKAROUND - if (state.nlen > 286 || state.ndist > 30) { - strm.msg = 'too many length or distance symbols'; - state.mode = BAD; - break; - } -//#endif - //Tracev((stderr, "inflate: table sizes ok\n")); - state.have = 0; - state.mode = LENLENS; - /* falls through */ - case LENLENS: - while (state.have < state.ncode) { - //=== NEEDBITS(3); - while (bits < 3) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); - //--- DROPBITS(3) ---// - hold >>>= 3; - bits -= 3; - //---// - } - while (state.have < 19) { - state.lens[order[state.have++]] = 0; - } - // We have separate tables & no pointers. 2 commented lines below not needed. - //state.next = state.codes; - //state.lencode = state.next; - // Switch to use dynamic table - state.lencode = state.lendyn; - state.lenbits = 7; - - opts = { bits: state.lenbits }; - ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); - state.lenbits = opts.bits; - - if (ret) { - strm.msg = 'invalid code lengths set'; - state.mode = BAD; - break; - } - //Tracev((stderr, "inflate: code lengths ok\n")); - state.have = 0; - state.mode = CODELENS; - /* falls through */ - case CODELENS: - while (state.have < state.nlen + state.ndist) { - for (;;) { - here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if (here_val < 16) { - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.lens[state.have++] = here_val; - } - else { - if (here_val === 16) { - //=== NEEDBITS(here.bits + 2); - n = here_bits + 2; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - if (state.have === 0) { - strm.msg = 'invalid bit length repeat'; - state.mode = BAD; - break; - } - len = state.lens[state.have - 1]; - copy = 3 + (hold & 0x03);//BITS(2); - //--- DROPBITS(2) ---// - hold >>>= 2; - bits -= 2; - //---// - } - else if (here_val === 17) { - //=== NEEDBITS(here.bits + 3); - n = here_bits + 3; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - len = 0; - copy = 3 + (hold & 0x07);//BITS(3); - //--- DROPBITS(3) ---// - hold >>>= 3; - bits -= 3; - //---// - } - else { - //=== NEEDBITS(here.bits + 7); - n = here_bits + 7; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - len = 0; - copy = 11 + (hold & 0x7f);//BITS(7); - //--- DROPBITS(7) ---// - hold >>>= 7; - bits -= 7; - //---// - } - if (state.have + copy > state.nlen + state.ndist) { - strm.msg = 'invalid bit length repeat'; - state.mode = BAD; - break; - } - while (copy--) { - state.lens[state.have++] = len; - } - } - } - - /* handle error breaks in while */ - if (state.mode === BAD) { break; } - - /* check for end-of-block code (better have one) */ - if (state.lens[256] === 0) { - strm.msg = 'invalid code -- missing end-of-block'; - state.mode = BAD; - break; - } - - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.h - concerning the ENOUGH constants, which depend on those values */ - state.lenbits = 9; - - opts = { bits: state.lenbits }; - ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); - // We have separate tables & no pointers. 2 commented lines below not needed. - // state.next_index = opts.table_index; - state.lenbits = opts.bits; - // state.lencode = state.next; - - if (ret) { - strm.msg = 'invalid literal/lengths set'; - state.mode = BAD; - break; - } - - state.distbits = 6; - //state.distcode.copy(state.codes); - // Switch to use dynamic table - state.distcode = state.distdyn; - opts = { bits: state.distbits }; - ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); - // We have separate tables & no pointers. 2 commented lines below not needed. - // state.next_index = opts.table_index; - state.distbits = opts.bits; - // state.distcode = state.next; - - if (ret) { - strm.msg = 'invalid distances set'; - state.mode = BAD; - break; - } - //Tracev((stderr, 'inflate: codes ok\n')); - state.mode = LEN_; - if (flush === Z_TREES) { break inf_leave; } - /* falls through */ - case LEN_: - state.mode = LEN; - /* falls through */ - case LEN: - if (have >= 6 && left >= 258) { - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - inflate_fast(strm, _out); - //--- LOAD() --- - put = strm.next_out; - output = strm.output; - left = strm.avail_out; - next = strm.next_in; - input = strm.input; - have = strm.avail_in; - hold = state.hold; - bits = state.bits; - //--- - - if (state.mode === TYPE) { - state.back = -1; - } - break; - } - state.back = 0; - for (;;) { - here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if (here_bits <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if (here_op && (here_op & 0xf0) === 0) { - last_bits = here_bits; - last_op = here_op; - last_val = here_val; - for (;;) { - here = state.lencode[last_val + - ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((last_bits + here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - //--- DROPBITS(last.bits) ---// - hold >>>= last_bits; - bits -= last_bits; - //---// - state.back += last_bits; - } - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.back += here_bits; - state.length = here_val; - if (here_op === 0) { - //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - // "inflate: literal '%c'\n" : - // "inflate: literal 0x%02x\n", here.val)); - state.mode = LIT; - break; - } - if (here_op & 32) { - //Tracevv((stderr, "inflate: end of block\n")); - state.back = -1; - state.mode = TYPE; - break; - } - if (here_op & 64) { - strm.msg = 'invalid literal/length code'; - state.mode = BAD; - break; - } - state.extra = here_op & 15; - state.mode = LENEXT; - /* falls through */ - case LENEXT: - if (state.extra) { - //=== NEEDBITS(state.extra); - n = state.extra; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; - //--- DROPBITS(state.extra) ---// - hold >>>= state.extra; - bits -= state.extra; - //---// - state.back += state.extra; - } - //Tracevv((stderr, "inflate: length %u\n", state.length)); - state.was = state.length; - state.mode = DIST; - /* falls through */ - case DIST: - for (;;) { - here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - if ((here_op & 0xf0) === 0) { - last_bits = here_bits; - last_op = here_op; - last_val = here_val; - for (;;) { - here = state.distcode[last_val + - ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; - here_bits = here >>> 24; - here_op = (here >>> 16) & 0xff; - here_val = here & 0xffff; - - if ((last_bits + here_bits) <= bits) { break; } - //--- PULLBYTE() ---// - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - //---// - } - //--- DROPBITS(last.bits) ---// - hold >>>= last_bits; - bits -= last_bits; - //---// - state.back += last_bits; - } - //--- DROPBITS(here.bits) ---// - hold >>>= here_bits; - bits -= here_bits; - //---// - state.back += here_bits; - if (here_op & 64) { - strm.msg = 'invalid distance code'; - state.mode = BAD; - break; - } - state.offset = here_val; - state.extra = (here_op) & 15; - state.mode = DISTEXT; - /* falls through */ - case DISTEXT: - if (state.extra) { - //=== NEEDBITS(state.extra); - n = state.extra; - while (bits < n) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; - //--- DROPBITS(state.extra) ---// - hold >>>= state.extra; - bits -= state.extra; - //---// - state.back += state.extra; - } -//#ifdef INFLATE_STRICT - if (state.offset > state.dmax) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break; - } -//#endif - //Tracevv((stderr, "inflate: distance %u\n", state.offset)); - state.mode = MATCH; - /* falls through */ - case MATCH: - if (left === 0) { break inf_leave; } - copy = _out - left; - if (state.offset > copy) { /* copy from window */ - copy = state.offset - copy; - if (copy > state.whave) { - if (state.sane) { - strm.msg = 'invalid distance too far back'; - state.mode = BAD; - break; - } -// (!) This block is disabled in zlib defaults, -// don't enable it for binary compatibility -//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR -// Trace((stderr, "inflate.c too far\n")); -// copy -= state.whave; -// if (copy > state.length) { copy = state.length; } -// if (copy > left) { copy = left; } -// left -= copy; -// state.length -= copy; -// do { -// output[put++] = 0; -// } while (--copy); -// if (state.length === 0) { state.mode = LEN; } -// break; -//#endif - } - if (copy > state.wnext) { - copy -= state.wnext; - from = state.wsize - copy; - } - else { - from = state.wnext - copy; - } - if (copy > state.length) { copy = state.length; } - from_source = state.window; - } - else { /* copy from output */ - from_source = output; - from = put - state.offset; - copy = state.length; - } - if (copy > left) { copy = left; } - left -= copy; - state.length -= copy; - do { - output[put++] = from_source[from++]; - } while (--copy); - if (state.length === 0) { state.mode = LEN; } - break; - case LIT: - if (left === 0) { break inf_leave; } - output[put++] = state.length; - left--; - state.mode = LEN; - break; - case CHECK: - if (state.wrap) { - //=== NEEDBITS(32); - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - // Use '|' instead of '+' to make sure that result is signed - hold |= input[next++] << bits; - bits += 8; - } - //===// - _out -= left; - strm.total_out += _out; - state.total += _out; - if (_out) { - strm.adler = state.check = - /*UPDATE(state.check, put - _out, _out);*/ - (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out)); - - } - _out = left; - // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too - if ((state.flags ? hold : zswap32(hold)) !== state.check) { - strm.msg = 'incorrect data check'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - //Tracev((stderr, "inflate: check matches trailer\n")); - } - state.mode = LENGTH; - /* falls through */ - case LENGTH: - if (state.wrap && state.flags) { - //=== NEEDBITS(32); - while (bits < 32) { - if (have === 0) { break inf_leave; } - have--; - hold += input[next++] << bits; - bits += 8; - } - //===// - if (hold !== (state.total & 0xffffffff)) { - strm.msg = 'incorrect length check'; - state.mode = BAD; - break; - } - //=== INITBITS(); - hold = 0; - bits = 0; - //===// - //Tracev((stderr, "inflate: length matches trailer\n")); - } - state.mode = DONE; - /* falls through */ - case DONE: - ret = Z_STREAM_END; - break inf_leave; - case BAD: - ret = Z_DATA_ERROR; - break inf_leave; - case MEM: - return Z_MEM_ERROR; - case SYNC: - /* falls through */ - default: - return Z_STREAM_ERROR; - } - } - - // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" - - /* - Return from inflate(), updating the total counts and the check value. - If there was no progress during the inflate() call, return a buffer - error. Call updatewindow() to create and/or update the window state. - Note: a memory error from inflate() is non-recoverable. - */ - - //--- RESTORE() --- - strm.next_out = put; - strm.avail_out = left; - strm.next_in = next; - strm.avail_in = have; - state.hold = hold; - state.bits = bits; - //--- - - if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && - (state.mode < CHECK || flush !== Z_FINISH))) { - if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) { - state.mode = MEM; - return Z_MEM_ERROR; - } - } - _in -= strm.avail_in; - _out -= strm.avail_out; - strm.total_in += _in; - strm.total_out += _out; - state.total += _out; - if (state.wrap && _out) { - strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ - (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out)); - } - strm.data_type = state.bits + (state.last ? 64 : 0) + - (state.mode === TYPE ? 128 : 0) + - (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); - if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) { - ret = Z_BUF_ERROR; - } - return ret; -}; - - -const inflateEnd = (strm) => { - - if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { - return Z_STREAM_ERROR; - } - - let state = strm.state; - if (state.window) { - state.window = null; - } - strm.state = null; - return Z_OK; -}; - - -const inflateGetHeader = (strm, head) => { - - /* check state */ - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - const state = strm.state; - if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; } - - /* save header structure */ - state.head = head; - head.done = false; - return Z_OK; -}; - - -const inflateSetDictionary = (strm, dictionary) => { - const dictLength = dictionary.length; - - let state; - let dictid; - let ret; - - /* check state */ - if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; } - state = strm.state; - - if (state.wrap !== 0 && state.mode !== DICT) { - return Z_STREAM_ERROR; - } - - /* check for correct dictionary identifier */ - if (state.mode === DICT) { - dictid = 1; /* adler32(0, null, 0)*/ - /* dictid = adler32(dictid, dictionary, dictLength); */ - dictid = adler32(dictid, dictionary, dictLength, 0); - if (dictid !== state.check) { - return Z_DATA_ERROR; - } - } - /* copy dictionary to window using updatewindow(), which will amend the - existing dictionary if appropriate */ - ret = updatewindow(strm, dictionary, dictLength, dictLength); - if (ret) { - state.mode = MEM; - return Z_MEM_ERROR; - } - state.havedict = 1; - // Tracev((stderr, "inflate: dictionary set\n")); - return Z_OK; -}; - - -module.exports.inflateReset = inflateReset; -module.exports.inflateReset2 = inflateReset2; -module.exports.inflateResetKeep = inflateResetKeep; -module.exports.inflateInit = inflateInit; -module.exports.inflateInit2 = inflateInit2; -module.exports.inflate = inflate; -module.exports.inflateEnd = inflateEnd; -module.exports.inflateGetHeader = inflateGetHeader; -module.exports.inflateSetDictionary = inflateSetDictionary; -module.exports.inflateInfo = 'pako inflate (from Nodeca project)'; - -/* Not implemented -module.exports.inflateCopy = inflateCopy; -module.exports.inflateGetDictionary = inflateGetDictionary; -module.exports.inflateMark = inflateMark; -module.exports.inflatePrime = inflatePrime; -module.exports.inflateSync = inflateSync; -module.exports.inflateSyncPoint = inflateSyncPoint; -module.exports.inflateUndermine = inflateUndermine; -*/ - -},{"./adler32":68,"./constants":69,"./crc32":70,"./inffast":73,"./inftrees":75}],75:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -const MAXBITS = 15; -const ENOUGH_LENS = 852; -const ENOUGH_DISTS = 592; -//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); - -const CODES = 0; -const LENS = 1; -const DISTS = 2; - -const lbase = new Uint16Array([ /* Length codes 257..285 base */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -]); - -const lext = new Uint8Array([ /* Length codes 257..285 extra */ - 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 -]); - -const dbase = new Uint16Array([ /* Distance codes 0..29 base */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577, 0, 0 -]); - -const dext = new Uint8Array([ /* Distance codes 0..29 extra */ - 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, - 28, 28, 29, 29, 64, 64 -]); - -const inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => -{ - const bits = opts.bits; - //here = opts.here; /* table entry for duplication */ - - let len = 0; /* a code's length in bits */ - let sym = 0; /* index of code symbols */ - let min = 0, max = 0; /* minimum and maximum code lengths */ - let root = 0; /* number of index bits for root table */ - let curr = 0; /* number of index bits for current table */ - let drop = 0; /* code bits to drop for sub-table */ - let left = 0; /* number of prefix codes available */ - let used = 0; /* code entries in table used */ - let huff = 0; /* Huffman code */ - let incr; /* for incrementing code, index */ - let fill; /* index for replicating entries */ - let low; /* low bits for current root entry */ - let mask; /* mask for low root bits */ - let next; /* next available space in table */ - let base = null; /* base value table to use */ - let base_index = 0; -// let shoextra; /* extra bits table to use */ - let end; /* use base and extra for symbol > end */ - const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ - const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ - let extra = null; - let extra_index = 0; - - let here_bits, here_op, here_val; - - /* - Process a set of code lengths to create a canonical Huffman code. The - code lengths are lens[0..codes-1]. Each length corresponds to the - symbols 0..codes-1. The Huffman code is generated by first sorting the - symbols by length from short to long, and retaining the symbol order - for codes with equal lengths. Then the code starts with all zero bits - for the first code of the shortest length, and the codes are integer - increments for the same length, and zeros are appended as the length - increases. For the deflate format, these bits are stored backwards - from their more natural integer increment ordering, and so when the - decoding tables are built in the large loop below, the integer codes - are incremented backwards. - - This routine assumes, but does not check, that all of the entries in - lens[] are in the range 0..MAXBITS. The caller must assure this. - 1..MAXBITS is interpreted as that code length. zero means that that - symbol does not occur in this code. - - The codes are sorted by computing a count of codes for each length, - creating from that a table of starting indices for each length in the - sorted table, and then entering the symbols in order in the sorted - table. The sorted table is work[], with that space being provided by - the caller. - - The length counts are used for other purposes as well, i.e. finding - the minimum and maximum length codes, determining if there are any - codes at all, checking for a valid set of lengths, and looking ahead - at length counts to determine sub-table sizes when building the - decoding tables. - */ - - /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ - for (len = 0; len <= MAXBITS; len++) { - count[len] = 0; - } - for (sym = 0; sym < codes; sym++) { - count[lens[lens_index + sym]]++; - } - - /* bound code lengths, force root to be within code lengths */ - root = bits; - for (max = MAXBITS; max >= 1; max--) { - if (count[max] !== 0) { break; } - } - if (root > max) { - root = max; - } - if (max === 0) { /* no symbols to code at all */ - //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ - //table.bits[opts.table_index] = 1; //here.bits = (var char)1; - //table.val[opts.table_index++] = 0; //here.val = (var short)0; - table[table_index++] = (1 << 24) | (64 << 16) | 0; - - - //table.op[opts.table_index] = 64; - //table.bits[opts.table_index] = 1; - //table.val[opts.table_index++] = 0; - table[table_index++] = (1 << 24) | (64 << 16) | 0; - - opts.bits = 1; - return 0; /* no symbols, but wait for decoding to report error */ - } - for (min = 1; min < max; min++) { - if (count[min] !== 0) { break; } - } - if (root < min) { - root = min; - } - - /* check for an over-subscribed or incomplete set of lengths */ - left = 1; - for (len = 1; len <= MAXBITS; len++) { - left <<= 1; - left -= count[len]; - if (left < 0) { - return -1; - } /* over-subscribed */ - } - if (left > 0 && (type === CODES || max !== 1)) { - return -1; /* incomplete set */ - } - - /* generate offsets into symbol table for each length for sorting */ - offs[1] = 0; - for (len = 1; len < MAXBITS; len++) { - offs[len + 1] = offs[len] + count[len]; - } - - /* sort symbols by length, by symbol order within each length */ - for (sym = 0; sym < codes; sym++) { - if (lens[lens_index + sym] !== 0) { - work[offs[lens[lens_index + sym]]++] = sym; - } - } - - /* - Create and fill in decoding tables. In this loop, the table being - filled is at next and has curr index bits. The code being used is huff - with length len. That code is converted to an index by dropping drop - bits off of the bottom. For codes where len is less than drop + curr, - those top drop + curr - len bits are incremented through all values to - fill the table with replicated entries. - - root is the number of index bits for the root table. When len exceeds - root, sub-tables are created pointed to by the root entry with an index - of the low root bits of huff. This is saved in low to check for when a - new sub-table should be started. drop is zero when the root table is - being filled, and drop is root when sub-tables are being filled. - - When a new sub-table is needed, it is necessary to look ahead in the - code lengths to determine what size sub-table is needed. The length - counts are used for this, and so count[] is decremented as codes are - entered in the tables. - - used keeps track of how many table entries have been allocated from the - provided *table space. It is checked for LENS and DIST tables against - the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in - the initial root table size constants. See the comments in inftrees.h - for more information. - - sym increments through all symbols, and the loop terminates when - all codes of length max, i.e. all codes, have been processed. This - routine permits incomplete codes, so another loop after this one fills - in the rest of the decoding tables with invalid code markers. - */ - - /* set up for code type */ - // poor man optimization - use if-else instead of switch, - // to avoid deopts in old v8 - if (type === CODES) { - base = extra = work; /* dummy value--not used */ - end = 19; - - } else if (type === LENS) { - base = lbase; - base_index -= 257; - extra = lext; - extra_index -= 257; - end = 256; - - } else { /* DISTS */ - base = dbase; - extra = dext; - end = -1; - } - - /* initialize opts for loop */ - huff = 0; /* starting code */ - sym = 0; /* starting code symbol */ - len = min; /* starting code length */ - next = table_index; /* current table to fill in */ - curr = root; /* current table index bits */ - drop = 0; /* current bits to drop from code for index */ - low = -1; /* trigger new sub-table when len > root */ - used = 1 << root; /* use root table entries */ - mask = used - 1; /* mask for comparing low */ - - /* check available table space */ - if ((type === LENS && used > ENOUGH_LENS) || - (type === DISTS && used > ENOUGH_DISTS)) { - return 1; - } - - /* process all codes and make table entries */ - for (;;) { - /* create table entry */ - here_bits = len - drop; - if (work[sym] < end) { - here_op = 0; - here_val = work[sym]; - } - else if (work[sym] > end) { - here_op = extra[extra_index + work[sym]]; - here_val = base[base_index + work[sym]]; - } - else { - here_op = 32 + 64; /* end of block */ - here_val = 0; - } - - /* replicate for those indices with low len bits equal to huff */ - incr = 1 << (len - drop); - fill = 1 << curr; - min = fill; /* save offset to next table */ - do { - fill -= incr; - table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; - } while (fill !== 0); - - /* backwards increment the len-bit code huff */ - incr = 1 << (len - 1); - while (huff & incr) { - incr >>= 1; - } - if (incr !== 0) { - huff &= incr - 1; - huff += incr; - } else { - huff = 0; - } - - /* go to next symbol, update count, len */ - sym++; - if (--count[len] === 0) { - if (len === max) { break; } - len = lens[lens_index + work[sym]]; - } - - /* create new sub-table if needed */ - if (len > root && (huff & mask) !== low) { - /* if first time, transition to sub-tables */ - if (drop === 0) { - drop = root; - } - - /* increment past last table */ - next += min; /* here min is 1 << curr */ - - /* determine length of next table */ - curr = len - drop; - left = 1 << curr; - while (curr + drop < max) { - left -= count[curr + drop]; - if (left <= 0) { break; } - curr++; - left <<= 1; - } - - /* check for enough space */ - used += 1 << curr; - if ((type === LENS && used > ENOUGH_LENS) || - (type === DISTS && used > ENOUGH_DISTS)) { - return 1; - } - - /* point entry in root table to sub-table */ - low = huff & mask; - /*table.op[low] = curr; - table.bits[low] = root; - table.val[low] = next - opts.table_index;*/ - table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; - } - } - - /* fill in remaining table entry if code is incomplete (guaranteed to have - at most one remaining entry, since if the code is incomplete, the - maximum code length that was allowed to get this far is one bit) */ - if (huff !== 0) { - //table.op[next + huff] = 64; /* invalid code marker */ - //table.bits[next + huff] = len - drop; - //table.val[next + huff] = 0; - table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; - } - - /* set return parameters */ - //opts.table_index += used; - opts.bits = root; - return 0; -}; - - -module.exports = inflate_table; - -},{}],76:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -module.exports = { - 2: 'need dictionary', /* Z_NEED_DICT 2 */ - 1: 'stream end', /* Z_STREAM_END 1 */ - 0: '', /* Z_OK 0 */ - '-1': 'file error', /* Z_ERRNO (-1) */ - '-2': 'stream error', /* Z_STREAM_ERROR (-2) */ - '-3': 'data error', /* Z_DATA_ERROR (-3) */ - '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */ - '-5': 'buffer error', /* Z_BUF_ERROR (-5) */ - '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ -}; - -},{}],77:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -/* eslint-disable space-unary-ops */ - -/* Public constants ==========================================================*/ -/* ===========================================================================*/ - - -//const Z_FILTERED = 1; -//const Z_HUFFMAN_ONLY = 2; -//const Z_RLE = 3; -const Z_FIXED = 4; -//const Z_DEFAULT_STRATEGY = 0; - -/* Possible values of the data_type field (though see inflate()) */ -const Z_BINARY = 0; -const Z_TEXT = 1; -//const Z_ASCII = 1; // = Z_TEXT -const Z_UNKNOWN = 2; - -/*============================================================================*/ - - -function zero(buf) { let len = buf.length; while (--len >= 0) { buf[len] = 0; } } - -// From zutil.h - -const STORED_BLOCK = 0; -const STATIC_TREES = 1; -const DYN_TREES = 2; -/* The three kinds of block type */ - -const MIN_MATCH = 3; -const MAX_MATCH = 258; -/* The minimum and maximum match lengths */ - -// From deflate.h -/* =========================================================================== - * Internal compression state. - */ - -const LENGTH_CODES = 29; -/* number of length codes, not counting the special END_BLOCK code */ - -const LITERALS = 256; -/* number of literal bytes 0..255 */ - -const L_CODES = LITERALS + 1 + LENGTH_CODES; -/* number of Literal or Length codes, including the END_BLOCK code */ - -const D_CODES = 30; -/* number of distance codes */ - -const BL_CODES = 19; -/* number of codes used to transfer the bit lengths */ - -const HEAP_SIZE = 2 * L_CODES + 1; -/* maximum heap size */ - -const MAX_BITS = 15; -/* All codes must not exceed MAX_BITS bits */ - -const Buf_size = 16; -/* size of bit buffer in bi_buf */ - - -/* =========================================================================== - * Constants - */ - -const MAX_BL_BITS = 7; -/* Bit length codes must not exceed MAX_BL_BITS bits */ - -const END_BLOCK = 256; -/* end of block literal code */ - -const REP_3_6 = 16; -/* repeat previous bit length 3-6 times (2 bits of repeat count) */ - -const REPZ_3_10 = 17; -/* repeat a zero length 3-10 times (3 bits of repeat count) */ - -const REPZ_11_138 = 18; -/* repeat a zero length 11-138 times (7 bits of repeat count) */ - -/* eslint-disable comma-spacing,array-bracket-spacing */ -const extra_lbits = /* extra bits for each length code */ - new Uint8Array([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]); - -const extra_dbits = /* extra bits for each distance code */ - new Uint8Array([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]); - -const extra_blbits = /* extra bits for each bit length code */ - new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]); - -const bl_order = - new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]); -/* eslint-enable comma-spacing,array-bracket-spacing */ - -/* The lengths of the bit length codes are sent in order of decreasing - * probability, to avoid transmitting the lengths for unused bit length codes. - */ - -/* =========================================================================== - * Local data. These are initialized only once. - */ - -// We pre-fill arrays with 0 to avoid uninitialized gaps - -const DIST_CODE_LEN = 512; /* see definition of array dist_code below */ - -// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 -const static_ltree = new Array((L_CODES + 2) * 2); -zero(static_ltree); -/* The static literal tree. Since the bit lengths are imposed, there is no - * need for the L_CODES extra codes used during heap construction. However - * The codes 286 and 287 are needed to build a canonical tree (see _tr_init - * below). - */ - -const static_dtree = new Array(D_CODES * 2); -zero(static_dtree); -/* The static distance tree. (Actually a trivial tree since all codes use - * 5 bits.) - */ - -const _dist_code = new Array(DIST_CODE_LEN); -zero(_dist_code); -/* Distance codes. The first 256 values correspond to the distances - * 3 .. 258, the last 256 values correspond to the top 8 bits of - * the 15 bit distances. - */ - -const _length_code = new Array(MAX_MATCH - MIN_MATCH + 1); -zero(_length_code); -/* length code for each normalized match length (0 == MIN_MATCH) */ - -const base_length = new Array(LENGTH_CODES); -zero(base_length); -/* First normalized length for each code (0 = MIN_MATCH) */ - -const base_dist = new Array(D_CODES); -zero(base_dist); -/* First normalized distance for each code (0 = distance of 1) */ - - -function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) { - - this.static_tree = static_tree; /* static tree or NULL */ - this.extra_bits = extra_bits; /* extra bits for each code or NULL */ - this.extra_base = extra_base; /* base index for extra_bits */ - this.elems = elems; /* max number of elements in the tree */ - this.max_length = max_length; /* max bit length for the codes */ - - // show if `static_tree` has data or dummy - needed for monomorphic objects - this.has_stree = static_tree && static_tree.length; -} - - -let static_l_desc; -let static_d_desc; -let static_bl_desc; - - -function TreeDesc(dyn_tree, stat_desc) { - this.dyn_tree = dyn_tree; /* the dynamic tree */ - this.max_code = 0; /* largest code with non zero frequency */ - this.stat_desc = stat_desc; /* the corresponding static tree */ -} - - - -const d_code = (dist) => { - - return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; -}; - - -/* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ -const put_short = (s, w) => { -// put_byte(s, (uch)((w) & 0xff)); -// put_byte(s, (uch)((ush)(w) >> 8)); - s.pending_buf[s.pending++] = (w) & 0xff; - s.pending_buf[s.pending++] = (w >>> 8) & 0xff; -}; - - -/* =========================================================================== - * Send a value on a given number of bits. - * IN assertion: length <= 16 and value fits in length bits. - */ -const send_bits = (s, value, length) => { - - if (s.bi_valid > (Buf_size - length)) { - s.bi_buf |= (value << s.bi_valid) & 0xffff; - put_short(s, s.bi_buf); - s.bi_buf = value >> (Buf_size - s.bi_valid); - s.bi_valid += length - Buf_size; - } else { - s.bi_buf |= (value << s.bi_valid) & 0xffff; - s.bi_valid += length; - } -}; - - -const send_code = (s, c, tree) => { - - send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/); -}; - - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -const bi_reverse = (code, len) => { - - let res = 0; - do { - res |= code & 1; - code >>>= 1; - res <<= 1; - } while (--len > 0); - return res >>> 1; -}; - - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -const bi_flush = (s) => { - - if (s.bi_valid === 16) { - put_short(s, s.bi_buf); - s.bi_buf = 0; - s.bi_valid = 0; - - } else if (s.bi_valid >= 8) { - s.pending_buf[s.pending++] = s.bi_buf & 0xff; - s.bi_buf >>= 8; - s.bi_valid -= 8; - } -}; - - -/* =========================================================================== - * Compute the optimal bit lengths for a tree and update the total bit length - * for the current block. - * IN assertion: the fields freq and dad are set, heap[heap_max] and - * above are the tree nodes sorted by increasing frequency. - * OUT assertions: the field len is set to the optimal bit length, the - * array bl_count contains the frequencies for each bit length. - * The length opt_len is updated; static_len is also updated if stree is - * not null. - */ -const gen_bitlen = (s, desc) => -// deflate_state *s; -// tree_desc *desc; /* the tree descriptor */ -{ - const tree = desc.dyn_tree; - const max_code = desc.max_code; - const stree = desc.stat_desc.static_tree; - const has_stree = desc.stat_desc.has_stree; - const extra = desc.stat_desc.extra_bits; - const base = desc.stat_desc.extra_base; - const max_length = desc.stat_desc.max_length; - let h; /* heap index */ - let n, m; /* iterate over the tree elements */ - let bits; /* bit length */ - let xbits; /* extra bits */ - let f; /* frequency */ - let overflow = 0; /* number of elements with bit length too large */ - - for (bits = 0; bits <= MAX_BITS; bits++) { - s.bl_count[bits] = 0; - } - - /* In a first pass, compute the optimal bit lengths (which may - * overflow in the case of the bit length tree). - */ - tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */ - - for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { - n = s.heap[h]; - bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; - if (bits > max_length) { - bits = max_length; - overflow++; - } - tree[n * 2 + 1]/*.Len*/ = bits; - /* We overwrite tree[n].Dad which is no longer needed */ - - if (n > max_code) { continue; } /* not a leaf node */ - - s.bl_count[bits]++; - xbits = 0; - if (n >= base) { - xbits = extra[n - base]; - } - f = tree[n * 2]/*.Freq*/; - s.opt_len += f * (bits + xbits); - if (has_stree) { - s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits); - } - } - if (overflow === 0) { return; } - - // Trace((stderr,"\nbit length overflow\n")); - /* This happens for example on obj2 and pic of the Calgary corpus */ - - /* Find the first bit length which could increase: */ - do { - bits = max_length - 1; - while (s.bl_count[bits] === 0) { bits--; } - s.bl_count[bits]--; /* move one leaf down the tree */ - s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ - s.bl_count[max_length]--; - /* The brother of the overflow item also moves one step up, - * but this does not affect bl_count[max_length] - */ - overflow -= 2; - } while (overflow > 0); - - /* Now recompute all bit lengths, scanning in increasing frequency. - * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all - * lengths instead of fixing only the wrong ones. This idea is taken - * from 'ar' written by Haruhiko Okumura.) - */ - for (bits = max_length; bits !== 0; bits--) { - n = s.bl_count[bits]; - while (n !== 0) { - m = s.heap[--h]; - if (m > max_code) { continue; } - if (tree[m * 2 + 1]/*.Len*/ !== bits) { - // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); - s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/; - tree[m * 2 + 1]/*.Len*/ = bits; - } - n--; - } - } -}; - - -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -const gen_codes = (tree, max_code, bl_count) => -// ct_data *tree; /* the tree to decorate */ -// int max_code; /* largest code with non zero frequency */ -// ushf *bl_count; /* number of codes at each bit length */ -{ - const next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */ - let code = 0; /* running code value */ - let bits; /* bit index */ - let n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits - 1]) << 1; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - //Assert (code + bl_count[MAX_BITS]-1 == (1< { - - let n; /* iterates over tree elements */ - let bits; /* bit counter */ - let length; /* length value */ - let code; /* code value */ - let dist; /* distance index */ - const bl_count = new Array(MAX_BITS + 1); - /* number of codes at each bit length for an optimal tree */ - - // do check in _tr_init() - //if (static_init_done) return; - - /* For some embedded targets, global variables are not initialized: */ -/*#ifdef NO_INIT_GLOBAL_POINTERS - static_l_desc.static_tree = static_ltree; - static_l_desc.extra_bits = extra_lbits; - static_d_desc.static_tree = static_dtree; - static_d_desc.extra_bits = extra_dbits; - static_bl_desc.extra_bits = extra_blbits; -#endif*/ - - /* Initialize the mapping length (0..255) -> length code (0..28) */ - length = 0; - for (code = 0; code < LENGTH_CODES - 1; code++) { - base_length[code] = length; - for (n = 0; n < (1 << extra_lbits[code]); n++) { - _length_code[length++] = code; - } - } - //Assert (length == 256, "tr_static_init: length != 256"); - /* Note that the length 255 (match length 258) can be represented - * in two different ways: code 284 + 5 bits or code 285, so we - * overwrite length_code[255] to use the best encoding: - */ - _length_code[length - 1] = code; - - /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ - dist = 0; - for (code = 0; code < 16; code++) { - base_dist[code] = dist; - for (n = 0; n < (1 << extra_dbits[code]); n++) { - _dist_code[dist++] = code; - } - } - //Assert (dist == 256, "tr_static_init: dist != 256"); - dist >>= 7; /* from now on, all distances are divided by 128 */ - for (; code < D_CODES; code++) { - base_dist[code] = dist << 7; - for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { - _dist_code[256 + dist++] = code; - } - } - //Assert (dist == 256, "tr_static_init: 256+dist != 512"); - - /* Construct the codes of the static literal tree */ - for (bits = 0; bits <= MAX_BITS; bits++) { - bl_count[bits] = 0; - } - - n = 0; - while (n <= 143) { - static_ltree[n * 2 + 1]/*.Len*/ = 8; - n++; - bl_count[8]++; - } - while (n <= 255) { - static_ltree[n * 2 + 1]/*.Len*/ = 9; - n++; - bl_count[9]++; - } - while (n <= 279) { - static_ltree[n * 2 + 1]/*.Len*/ = 7; - n++; - bl_count[7]++; - } - while (n <= 287) { - static_ltree[n * 2 + 1]/*.Len*/ = 8; - n++; - bl_count[8]++; - } - /* Codes 286 and 287 do not exist, but we must include them in the - * tree construction to get a canonical Huffman tree (longest code - * all ones) - */ - gen_codes(static_ltree, L_CODES + 1, bl_count); - - /* The static distance tree is trivial: */ - for (n = 0; n < D_CODES; n++) { - static_dtree[n * 2 + 1]/*.Len*/ = 5; - static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5); - } - - // Now data ready and we can init static trees - static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); - static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); - static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); - - //static_init_done = true; -}; - - -/* =========================================================================== - * Initialize a new block. - */ -const init_block = (s) => { - - let n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } - for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } - for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } - - s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1; - s.opt_len = s.static_len = 0; - s.last_lit = s.matches = 0; -}; - - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -const bi_windup = (s) => -{ - if (s.bi_valid > 8) { - put_short(s, s.bi_buf); - } else if (s.bi_valid > 0) { - //put_byte(s, (Byte)s->bi_buf); - s.pending_buf[s.pending++] = s.bi_buf; - } - s.bi_buf = 0; - s.bi_valid = 0; -}; - -/* =========================================================================== - * Copy a stored block, storing first the length and its - * one's complement if requested. - */ -const copy_block = (s, buf, len, header) => -//DeflateState *s; -//charf *buf; /* the input data */ -//unsigned len; /* its length */ -//int header; /* true if block header must be written */ -{ - bi_windup(s); /* align on byte boundary */ - - if (header) { - put_short(s, len); - put_short(s, ~len); - } -// while (len--) { -// put_byte(s, *buf++); -// } - s.pending_buf.set(s.window.subarray(buf, buf + len), s.pending); - s.pending += len; -}; - -/* =========================================================================== - * Compares to subtrees, using the tree depth as tie breaker when - * the subtrees have equal frequency. This minimizes the worst case length. - */ -const smaller = (tree, n, m, depth) => { - - const _n2 = n * 2; - const _m2 = m * 2; - return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || - (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); -}; - -/* =========================================================================== - * Restore the heap property by moving down the tree starting at node k, - * exchanging a node with the smallest of its two sons if necessary, stopping - * when the heap property is re-established (each father smaller than its - * two sons). - */ -const pqdownheap = (s, tree, k) => -// deflate_state *s; -// ct_data *tree; /* the tree to restore */ -// int k; /* node to move down */ -{ - const v = s.heap[k]; - let j = k << 1; /* left son of k */ - while (j <= s.heap_len) { - /* Set j to the smallest of the two sons: */ - if (j < s.heap_len && - smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) { - j++; - } - /* Exit if v is smaller than both sons */ - if (smaller(tree, v, s.heap[j], s.depth)) { break; } - - /* Exchange v with the smallest son */ - s.heap[k] = s.heap[j]; - k = j; - - /* And continue down the tree, setting j to the left son of k */ - j <<= 1; - } - s.heap[k] = v; -}; - - -// inlined manually -// const SMALLEST = 1; - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -const compress_block = (s, ltree, dtree) => -// deflate_state *s; -// const ct_data *ltree; /* literal tree */ -// const ct_data *dtree; /* distance tree */ -{ - let dist; /* distance of matched string */ - let lc; /* match length or unmatched char (if dist == 0) */ - let lx = 0; /* running index in l_buf */ - let code; /* the code to send */ - let extra; /* number of extra bits to send */ - - if (s.last_lit !== 0) { - do { - dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]); - lc = s.pending_buf[s.l_buf + lx]; - lx++; - - if (dist === 0) { - send_code(s, lc, ltree); /* send a literal byte */ - //Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code + LITERALS + 1, ltree); /* send the length code */ - extra = extra_lbits[code]; - if (extra !== 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - //Assert (code < D_CODES, "bad d_code"); - - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; - if (extra !== 0) { - dist -= base_dist[code]; - send_bits(s, dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ - //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, - // "pendingBuf overflow"); - - } while (lx < s.last_lit); - } - - send_code(s, END_BLOCK, ltree); -}; - - -/* =========================================================================== - * Construct one Huffman tree and assigns the code bit strings and lengths. - * Update the total bit length for the current block. - * IN assertion: the field freq is set for all tree elements. - * OUT assertions: the fields len and code are set to the optimal bit length - * and corresponding code. The length opt_len is updated; static_len is - * also updated if stree is not null. The field max_code is set. - */ -const build_tree = (s, desc) => -// deflate_state *s; -// tree_desc *desc; /* the tree descriptor */ -{ - const tree = desc.dyn_tree; - const stree = desc.stat_desc.static_tree; - const has_stree = desc.stat_desc.has_stree; - const elems = desc.stat_desc.elems; - let n, m; /* iterate over heap elements */ - let max_code = -1; /* largest code with non zero frequency */ - let node; /* new node being created */ - - /* Construct the initial heap, with least frequent element in - * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - * heap[0] is not used. - */ - s.heap_len = 0; - s.heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) { - if (tree[n * 2]/*.Freq*/ !== 0) { - s.heap[++s.heap_len] = max_code = n; - s.depth[n] = 0; - - } else { - tree[n * 2 + 1]/*.Len*/ = 0; - } - } - - /* The pkzip format requires that at least one distance code exists, - * and that at least one bit should be sent even if there is only one - * possible code. So to avoid special checks later on we force at least - * two codes of non zero frequency. - */ - while (s.heap_len < 2) { - node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); - tree[node * 2]/*.Freq*/ = 1; - s.depth[node] = 0; - s.opt_len--; - - if (has_stree) { - s.static_len -= stree[node * 2 + 1]/*.Len*/; - } - /* node is 0 or 1 so it does not have extra bits */ - } - desc.max_code = max_code; - - /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - * establish sub-heaps of increasing lengths: - */ - for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); } - - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ - node = elems; /* next internal node of the tree */ - do { - //pqremove(s, tree, n); /* n = node of least frequency */ - /*** pqremove ***/ - n = s.heap[1/*SMALLEST*/]; - s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; - pqdownheap(s, tree, 1/*SMALLEST*/); - /***/ - - m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ - - s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ - s.heap[--s.heap_max] = m; - - /* Create a new node father of n and m */ - tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; - s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; - tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node; - - /* and insert the new node in the heap */ - s.heap[1/*SMALLEST*/] = node++; - pqdownheap(s, tree, 1/*SMALLEST*/); - - } while (s.heap_len >= 2); - - s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; - - /* At this point, the fields freq and dad are set. We can now - * generate the bit lengths. - */ - gen_bitlen(s, desc); - - /* The field len is now set, we can generate the bit codes */ - gen_codes(tree, max_code, s.bl_count); -}; - - -/* =========================================================================== - * Scan a literal or distance tree to determine the frequencies of the codes - * in the bit length tree. - */ -const scan_tree = (s, tree, max_code) => -// deflate_state *s; -// ct_data *tree; /* the tree to be scanned */ -// int max_code; /* and its largest code of non zero frequency */ -{ - let n; /* iterates over all tree elements */ - let prevlen = -1; /* last emitted length */ - let curlen; /* length of current code */ - - let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ - - let count = 0; /* repeat count of the current code */ - let max_count = 7; /* max repeat count */ - let min_count = 4; /* min repeat count */ - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */ - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; - - if (++count < max_count && curlen === nextlen) { - continue; - - } else if (count < min_count) { - s.bl_tree[curlen * 2]/*.Freq*/ += count; - - } else if (curlen !== 0) { - - if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; } - s.bl_tree[REP_3_6 * 2]/*.Freq*/++; - - } else if (count <= 10) { - s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++; - - } else { - s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++; - } - - count = 0; - prevlen = curlen; - - if (nextlen === 0) { - max_count = 138; - min_count = 3; - - } else if (curlen === nextlen) { - max_count = 6; - min_count = 3; - - } else { - max_count = 7; - min_count = 4; - } - } -}; - - -/* =========================================================================== - * Send a literal or distance tree in compressed form, using the codes in - * bl_tree. - */ -const send_tree = (s, tree, max_code) => -// deflate_state *s; -// ct_data *tree; /* the tree to be scanned */ -// int max_code; /* and its largest code of non zero frequency */ -{ - let n; /* iterates over all tree elements */ - let prevlen = -1; /* last emitted length */ - let curlen; /* length of current code */ - - let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ - - let count = 0; /* repeat count of the current code */ - let max_count = 7; /* max repeat count */ - let min_count = 4; /* min repeat count */ - - /* tree[max_code+1].Len = -1; */ /* guard already set */ - if (nextlen === 0) { - max_count = 138; - min_count = 3; - } - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; - - if (++count < max_count && curlen === nextlen) { - continue; - - } else if (count < min_count) { - do { send_code(s, curlen, s.bl_tree); } while (--count !== 0); - - } else if (curlen !== 0) { - if (curlen !== prevlen) { - send_code(s, curlen, s.bl_tree); - count--; - } - //Assert(count >= 3 && count <= 6, " 3_6?"); - send_code(s, REP_3_6, s.bl_tree); - send_bits(s, count - 3, 2); - - } else if (count <= 10) { - send_code(s, REPZ_3_10, s.bl_tree); - send_bits(s, count - 3, 3); - - } else { - send_code(s, REPZ_11_138, s.bl_tree); - send_bits(s, count - 11, 7); - } - - count = 0; - prevlen = curlen; - if (nextlen === 0) { - max_count = 138; - min_count = 3; - - } else if (curlen === nextlen) { - max_count = 6; - min_count = 3; - - } else { - max_count = 7; - min_count = 4; - } - } -}; - - -/* =========================================================================== - * Construct the Huffman tree for the bit lengths and return the index in - * bl_order of the last bit length code to send. - */ -const build_bl_tree = (s) => { - - let max_blindex; /* index of last bit length code of non zero freq */ - - /* Determine the bit length frequencies for literal and distance trees */ - scan_tree(s, s.dyn_ltree, s.l_desc.max_code); - scan_tree(s, s.dyn_dtree, s.d_desc.max_code); - - /* Build the bit length tree: */ - build_tree(s, s.bl_desc); - /* opt_len now includes the length of the tree representations, except - * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. - */ - - /* Determine the number of bit length codes to send. The pkzip format - * requires that at least 4 bit length codes be sent. (appnote.txt says - * 3 but the actual value used is 4.) - */ - for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { - if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) { - break; - } - } - /* Update opt_len to include the bit length tree and counts */ - s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; - //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", - // s->opt_len, s->static_len)); - - return max_blindex; -}; - - -/* =========================================================================== - * Send the header for a block using dynamic Huffman trees: the counts, the - * lengths of the bit length codes, the literal tree and the distance tree. - * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - */ -const send_all_trees = (s, lcodes, dcodes, blcodes) => -// deflate_state *s; -// int lcodes, dcodes, blcodes; /* number of codes for each tree */ -{ - let rank; /* index in bl_order */ - - //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); - //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, - // "too many codes"); - //Tracev((stderr, "\nbl counts: ")); - send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ - send_bits(s, dcodes - 1, 5); - send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ - for (rank = 0; rank < blcodes; rank++) { - //Tracev((stderr, "\nbl code %2d ", bl_order[rank])); - send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3); - } - //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); - - send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */ - //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); - - send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */ - //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); -}; - - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "black list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -const detect_data_type = (s) => { - /* black_mask is the bit mask of black-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - let black_mask = 0xf3ffc07f; - let n; - - /* Check for non-textual ("black-listed") bytes. */ - for (n = 0; n <= 31; n++, black_mask >>>= 1) { - if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) { - return Z_BINARY; - } - } - - /* Check for textual ("white-listed") bytes. */ - if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 || - s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { - return Z_TEXT; - } - for (n = 32; n < LITERALS; n++) { - if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { - return Z_TEXT; - } - } - - /* There are no "black-listed" or "white-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -}; - - -let static_init_done = false; - -/* =========================================================================== - * Initialize the tree data structures for a new zlib stream. - */ -const _tr_init = (s) => -{ - - if (!static_init_done) { - tr_static_init(); - static_init_done = true; - } - - s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); - s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); - s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); - - s.bi_buf = 0; - s.bi_valid = 0; - - /* Initialize the first block of the first file: */ - init_block(s); -}; - - -/* =========================================================================== - * Send a stored block - */ -const _tr_stored_block = (s, buf, stored_len, last) => -//DeflateState *s; -//charf *buf; /* input block */ -//ulg stored_len; /* length of input block */ -//int last; /* one if this is the last block for a file */ -{ - send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */ - copy_block(s, buf, stored_len, true); /* with header */ -}; - - -/* =========================================================================== - * Send one empty static block to give enough lookahead for inflate. - * This takes 10 bits, of which 7 may remain in the bit buffer. - */ -const _tr_align = (s) => { - send_bits(s, STATIC_TREES << 1, 3); - send_code(s, END_BLOCK, static_ltree); - bi_flush(s); -}; - - -/* =========================================================================== - * Determine the best encoding for the current block: dynamic trees, static - * trees or store, and output the encoded block to the zip file. - */ -const _tr_flush_block = (s, buf, stored_len, last) => -//DeflateState *s; -//charf *buf; /* input block, or NULL if too old */ -//ulg stored_len; /* length of input block */ -//int last; /* one if this is the last block for a file */ -{ - let opt_lenb, static_lenb; /* opt_len and static_len in bytes */ - let max_blindex = 0; /* index of last bit length code of non zero freq */ - - /* Build the Huffman trees unless a stored block is forced */ - if (s.level > 0) { - - /* Check if the file is binary or text */ - if (s.strm.data_type === Z_UNKNOWN) { - s.strm.data_type = detect_data_type(s); - } - - /* Construct the literal and distance trees */ - build_tree(s, s.l_desc); - // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, - // s->static_len)); - - build_tree(s, s.d_desc); - // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, - // s->static_len)); - /* At this point, opt_len and static_len are the total bit lengths of - * the compressed block data, excluding the tree representations. - */ - - /* Build the bit length tree for the above two trees, and get the index - * in bl_order of the last bit length code to send. - */ - max_blindex = build_bl_tree(s); - - /* Determine the best encoding. Compute the block lengths in bytes. */ - opt_lenb = (s.opt_len + 3 + 7) >>> 3; - static_lenb = (s.static_len + 3 + 7) >>> 3; - - // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", - // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, - // s->last_lit)); - - if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } - - } else { - // Assert(buf != (char*)0, "lost buf"); - opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ - } - - if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) { - /* 4: two words for the lengths */ - - /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - * Otherwise we can't have processed more than WSIZE input bytes since - * the last block flush, because compression would have been - * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - * transform a block into a stored block. - */ - _tr_stored_block(s, buf, stored_len, last); - - } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) { - - send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); - compress_block(s, static_ltree, static_dtree); - - } else { - send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); - send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1); - compress_block(s, s.dyn_ltree, s.dyn_dtree); - } - // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); - /* The above check is made mod 2^32, for files larger than 512 MB - * and uLong implemented on 32 bits. - */ - init_block(s); - - if (last) { - bi_windup(s); - } - // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, - // s->compressed_len-7*last)); -}; - -/* =========================================================================== - * Save the match info and tally the frequency counts. Return true if - * the current block must be flushed. - */ -const _tr_tally = (s, dist, lc) => -// deflate_state *s; -// unsigned dist; /* distance of matched string */ -// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ -{ - //let out_length, in_length, dcode; - - s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; - s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; - - s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; - s.last_lit++; - - if (dist === 0) { - /* lc is the unmatched char */ - s.dyn_ltree[lc * 2]/*.Freq*/++; - } else { - s.matches++; - /* Here, lc is the match length - MIN_MATCH */ - dist--; /* dist = match distance - 1 */ - //Assert((ush)dist < (ush)MAX_DIST(s) && - // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && - // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); - - s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++; - s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; - } - -// (!) This block is disabled in zlib defaults, -// don't enable it for binary compatibility - -//#ifdef TRUNCATE_BLOCK -// /* Try to guess if it is profitable to stop the current block here */ -// if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { -// /* Compute an upper bound for the compressed length */ -// out_length = s.last_lit*8; -// in_length = s.strstart - s.block_start; -// -// for (dcode = 0; dcode < D_CODES; dcode++) { -// out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); -// } -// out_length >>>= 3; -// //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", -// // s->last_lit, in_length, out_length, -// // 100L - out_length*100L/in_length)); -// if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { -// return true; -// } -// } -//#endif - - return (s.last_lit === s.lit_bufsize - 1); - /* We avoid equality with lit_bufsize because of wraparound at 64K - * on 16 bit machines and because stored blocks are restricted to - * 64K-1 bytes. - */ -}; - -module.exports._tr_init = _tr_init; -module.exports._tr_stored_block = _tr_stored_block; -module.exports._tr_flush_block = _tr_flush_block; -module.exports._tr_tally = _tr_tally; -module.exports._tr_align = _tr_align; - -},{}],78:[function(require,module,exports){ -'use strict'; - -// (C) 1995-2013 Jean-loup Gailly and Mark Adler -// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -function ZStream() { - /* next input byte */ - this.input = null; // JS specific, because we have no pointers - this.next_in = 0; - /* number of bytes available at input */ - this.avail_in = 0; - /* total number of input bytes read so far */ - this.total_in = 0; - /* next output byte should be put there */ - this.output = null; // JS specific, because we have no pointers - this.next_out = 0; - /* remaining free space at output */ - this.avail_out = 0; - /* total number of bytes output so far */ - this.total_out = 0; - /* last error message, NULL if no error */ - this.msg = ''/*Z_NULL*/; - /* not visible by applications */ - this.state = null; - /* best guess about the data type: binary or text */ - this.data_type = 2/*Z_UNKNOWN*/; - /* adler32 value of the uncompressed data */ - this.adler = 0; -} - -module.exports = ZStream; - -},{}],79:[function(require,module,exports){ -var v1 = require('./v1'); -var v4 = require('./v4'); - -var uuid = v4; -uuid.v1 = v1; -uuid.v4 = v4; - -module.exports = uuid; - -},{"./v1":82,"./v4":83}],80:[function(require,module,exports){ -/** - * Convert array of 16 byte values to UUID string format of the form: - * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - */ -var byteToHex = []; -for (var i = 0; i < 256; ++i) { - byteToHex[i] = (i + 0x100).toString(16).substr(1); -} - -function bytesToUuid(buf, offset) { - var i = offset || 0; - var bth = byteToHex; - // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4 - return ([ - bth[buf[i++]], bth[buf[i++]], - bth[buf[i++]], bth[buf[i++]], '-', - bth[buf[i++]], bth[buf[i++]], '-', - bth[buf[i++]], bth[buf[i++]], '-', - bth[buf[i++]], bth[buf[i++]], '-', - bth[buf[i++]], bth[buf[i++]], - bth[buf[i++]], bth[buf[i++]], - bth[buf[i++]], bth[buf[i++]] - ]).join(''); -} - -module.exports = bytesToUuid; - -},{}],81:[function(require,module,exports){ -// Unique ID creation requires a high quality random # generator. In the -// browser this is a little complicated due to unknown quality of Math.random() -// and inconsistent support for the `crypto` API. We do the best we can via -// feature-detection - -// getRandomValues needs to be invoked in a context where "this" is a Crypto -// implementation. Also, find the complete implementation of crypto on IE11. -var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) || - (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto)); - -if (getRandomValues) { - // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto - var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef - - module.exports = function whatwgRNG() { - getRandomValues(rnds8); - return rnds8; - }; -} else { - // Math.random()-based (RNG) - // - // If all else fails, use Math.random(). It's fast, but is of unspecified - // quality. - var rnds = new Array(16); - - module.exports = function mathRNG() { - for (var i = 0, r; i < 16; i++) { - if ((i & 0x03) === 0) r = Math.random() * 0x100000000; - rnds[i] = r >>> ((i & 0x03) << 3) & 0xff; - } - - return rnds; - }; -} - -},{}],82:[function(require,module,exports){ -var rng = require('./lib/rng'); -var bytesToUuid = require('./lib/bytesToUuid'); - -// **`v1()` - Generate time-based UUID** -// -// Inspired by https://github.com/LiosK/UUID.js -// and http://docs.python.org/library/uuid.html - -var _nodeId; -var _clockseq; - -// Previous uuid creation time -var _lastMSecs = 0; -var _lastNSecs = 0; - -// See https://github.com/uuidjs/uuid for API details -function v1(options, buf, offset) { - var i = buf && offset || 0; - var b = buf || []; - - options = options || {}; - var node = options.node || _nodeId; - var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; - - // node and clockseq need to be initialized to random values if they're not - // specified. We do this lazily to minimize issues related to insufficient - // system entropy. See #189 - if (node == null || clockseq == null) { - var seedBytes = rng(); - if (node == null) { - // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) - node = _nodeId = [ - seedBytes[0] | 0x01, - seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5] - ]; - } - if (clockseq == null) { - // Per 4.2.2, randomize (14 bit) clockseq - clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; - } - } - - // UUID timestamps are 100 nano-second units since the Gregorian epoch, - // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so - // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' - // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. - var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime(); - - // Per 4.2.1.2, use count of uuid's generated during the current clock - // cycle to simulate higher resolution clock - var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; - - // Time since last uuid creation (in msecs) - var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000; - - // Per 4.2.1.2, Bump clockseq on clock regression - if (dt < 0 && options.clockseq === undefined) { - clockseq = clockseq + 1 & 0x3fff; - } - - // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new - // time interval - if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { - nsecs = 0; - } - - // Per 4.2.1.2 Throw error if too many uuids are requested - if (nsecs >= 10000) { - throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec'); - } - - _lastMSecs = msecs; - _lastNSecs = nsecs; - _clockseq = clockseq; - - // Per 4.1.4 - Convert from unix epoch to Gregorian epoch - msecs += 12219292800000; - - // `time_low` - var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; - b[i++] = tl >>> 24 & 0xff; - b[i++] = tl >>> 16 & 0xff; - b[i++] = tl >>> 8 & 0xff; - b[i++] = tl & 0xff; - - // `time_mid` - var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff; - b[i++] = tmh >>> 8 & 0xff; - b[i++] = tmh & 0xff; - - // `time_high_and_version` - b[i++] = tmh >>> 24 & 0xf | 0x10; // include version - b[i++] = tmh >>> 16 & 0xff; - - // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) - b[i++] = clockseq >>> 8 | 0x80; - - // `clock_seq_low` - b[i++] = clockseq & 0xff; - - // `node` - for (var n = 0; n < 6; ++n) { - b[i + n] = node[n]; - } - - return buf ? buf : bytesToUuid(b); -} - -module.exports = v1; - -},{"./lib/bytesToUuid":80,"./lib/rng":81}],83:[function(require,module,exports){ -var rng = require('./lib/rng'); -var bytesToUuid = require('./lib/bytesToUuid'); - -function v4(options, buf, offset) { - var i = buf && offset || 0; - - if (typeof(options) == 'string') { - buf = options === 'binary' ? new Array(16) : null; - options = null; - } - options = options || {}; - - var rnds = options.random || (options.rng || rng)(); - - // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` - rnds[6] = (rnds[6] & 0x0f) | 0x40; - rnds[8] = (rnds[8] & 0x3f) | 0x80; - - // Copy bytes to buffer, if provided - if (buf) { - for (var ii = 0; ii < 16; ++ii) { - buf[i + ii] = rnds[ii]; - } - } - - return buf || bytesToUuid(rnds); -} - -module.exports = v4; - -},{"./lib/bytesToUuid":80,"./lib/rng":81}],84:[function(require,module,exports){ -const uuid = require('./uuid') -const Frontend = require('../frontend') -const { OPTIONS } = require('../frontend/constants') -const { encodeChange, decodeChange } = require('../backend/columnar') -const { isObject } = require('./common') -let backend = require('../backend') // mutable: can be overridden with setDefaultBackend() - -/** - * Automerge.* API - * The functions in this file constitute the publicly facing Automerge API which combines - * the features of the Frontend (a document interface) and the backend (CRDT operations) - */ - -function init(options) { - if (typeof options === 'string') { - options = {actorId: options} - } else if (typeof options === 'undefined') { - options = {} - } else if (!isObject(options)) { - throw new TypeError(`Unsupported options for init(): ${options}`) - } - return Frontend.init(Object.assign({backend}, options)) -} - -/** - * Returns a new document object initialized with the given state. - */ -function from(initialState, options) { - const changeOpts = {message: 'Initialization'} - return change(init(options), changeOpts, doc => Object.assign(doc, initialState)) -} - -function change(doc, options, callback) { - const [newDoc] = Frontend.change(doc, options, callback) - return newDoc -} - -function emptyChange(doc, options) { - const [newDoc] = Frontend.emptyChange(doc, options) - return newDoc -} - -function clone(doc, options = {}) { - const state = backend.clone(Frontend.getBackendState(doc, 'clone')) - return applyPatch(init(options), backend.getPatch(state), state, [], options) -} - -function free(doc) { - backend.free(Frontend.getBackendState(doc, 'free')) -} - -function load(data, options = {}) { - const state = backend.load(data) - return applyPatch(init(options), backend.getPatch(state), state, [data], options) -} - -function save(doc) { - return backend.save(Frontend.getBackendState(doc, 'save')) -} - -function merge(localDoc, remoteDoc) { - const localState = Frontend.getBackendState(localDoc, 'merge') - const remoteState = Frontend.getBackendState(remoteDoc, 'merge', 'second') - if (Frontend.getActorId(localDoc) === Frontend.getActorId(remoteDoc)) { - throw new RangeError('Cannot merge an actor with itself') - } - const changes = backend.getChangesAdded(localState, remoteState) - const [updatedDoc] = applyChanges(localDoc, changes) - return updatedDoc -} - -function getChanges(oldDoc, newDoc) { - const oldState = Frontend.getBackendState(oldDoc, 'getChanges') - const newState = Frontend.getBackendState(newDoc, 'getChanges', 'second') - return backend.getChanges(newState, backend.getHeads(oldState)) -} - -function getAllChanges(doc) { - return backend.getAllChanges(Frontend.getBackendState(doc, 'getAllChanges')) -} - -function applyPatch(doc, patch, backendState, changes, options) { - const newDoc = Frontend.applyPatch(doc, patch, backendState) - const patchCallback = options.patchCallback || doc[OPTIONS].patchCallback - if (patchCallback) { - patchCallback(patch, doc, newDoc, false, changes) - } - return newDoc -} - -function applyChanges(doc, changes, options = {}) { - const oldState = Frontend.getBackendState(doc, 'applyChanges') - const [newState, patch] = backend.applyChanges(oldState, changes) - return [applyPatch(doc, patch, newState, changes, options), patch] -} - -function equals(val1, val2) { - if (!isObject(val1) || !isObject(val2)) return val1 === val2 - const keys1 = Object.keys(val1).sort(), keys2 = Object.keys(val2).sort() - if (keys1.length !== keys2.length) return false - for (let i = 0; i < keys1.length; i++) { - if (keys1[i] !== keys2[i]) return false - if (!equals(val1[keys1[i]], val2[keys2[i]])) return false - } - return true -} - -function getHistory(doc) { - const actor = Frontend.getActorId(doc) - const history = getAllChanges(doc) - return history.map((change, index) => ({ - get change () { - return decodeChange(change) - }, - get snapshot () { - const state = backend.loadChanges(backend.init(), history.slice(0, index + 1)) - return Frontend.applyPatch(init(actor), backend.getPatch(state), state) - } - }) - ) -} - -function generateSyncMessage(doc, syncState) { - const state = Frontend.getBackendState(doc, 'generateSyncMessage') - return backend.generateSyncMessage(state, syncState) -} - -function receiveSyncMessage(doc, oldSyncState, message) { - const oldBackendState = Frontend.getBackendState(doc, 'receiveSyncMessage') - const [backendState, syncState, patch] = backend.receiveSyncMessage(oldBackendState, oldSyncState, message) - if (!patch) return [doc, syncState, patch] - - // The patchCallback is passed as argument all changes that are applied. - // We get those from the sync message if a patchCallback is present. - let changes = null - if (doc[OPTIONS].patchCallback) { - changes = backend.decodeSyncMessage(message).changes - } - return [applyPatch(doc, patch, backendState, changes, {}), syncState, patch] -} - -function initSyncState() { - return backend.initSyncState() -} - -/** - * Replaces the default backend implementation with a different one. - * This allows you to switch to using the Rust/WebAssembly implementation. - */ -function setDefaultBackend(newBackend) { - backend = newBackend -} - -module.exports = { - init, from, change, emptyChange, clone, free, - load, save, merge, getChanges, getAllChanges, applyChanges, - encodeChange, decodeChange, equals, getHistory, uuid, - Frontend, setDefaultBackend, generateSyncMessage, receiveSyncMessage, initSyncState, - get Backend() { return backend } -} - -for (let name of ['getObjectId', 'getObjectById', 'getActorId', - 'setActorId', 'getConflicts', 'getLastLocalChange', - 'Text', 'Table', 'Counter', 'Observable', 'Int', 'Uint', 'Float64']) { - module.exports[name] = Frontend[name] -} - -},{"../backend":49,"../backend/columnar":47,"../frontend":57,"../frontend/constants":54,"./common":85,"./uuid":86}],85:[function(require,module,exports){ -function isObject(obj) { - return typeof obj === 'object' && obj !== null -} - -/** - * Returns a shallow copy of the object `obj`. Faster than `Object.assign({}, obj)`. - * https://jsperf.com/cloning-large-objects/1 - */ -function copyObject(obj) { - if (!isObject(obj)) return {} - let copy = {} - for (let key of Object.keys(obj)) { - copy[key] = obj[key] - } - return copy -} - -/** - * Takes a string in the form that is used to identify operations (a counter concatenated - * with an actor ID, separated by an `@` sign) and returns an object `{counter, actorId}`. - */ -function parseOpId(opId) { - const match = /^(\d+)@(.*)$/.exec(opId || '') - if (!match) { - throw new RangeError(`Not a valid opId: ${opId}`) - } - return {counter: parseInt(match[1], 10), actorId: match[2]} -} - -/** - * Returns true if the two byte arrays contain the same data, false if not. - */ -function equalBytes(array1, array2) { - if (!(array1 instanceof Uint8Array) || !(array2 instanceof Uint8Array)) { - throw new TypeError('equalBytes can only compare Uint8Arrays') - } - if (array1.byteLength !== array2.byteLength) return false - for (let i = 0; i < array1.byteLength; i++) { - if (array1[i] !== array2[i]) return false - } - return true -} - -/** - * Creates an array containing the value `null` repeated `length` times. - */ -function createArrayOfNulls(length) { - const array = new Array(length) - for (let i = 0; i < length; i++) array[i] = null - return array -} - -module.exports = { - isObject, copyObject, parseOpId, equalBytes, createArrayOfNulls -} - -},{}],86:[function(require,module,exports){ -const { v4: uuid } = require('uuid') - -function defaultFactory() { - return uuid().replace(/-/g, '') -} - -let factory = defaultFactory - -function makeUuid() { - return factory() -} - -makeUuid.setFactory = newFactory => { factory = newFactory } -makeUuid.reset = () => { factory = defaultFactory } - -module.exports = makeUuid - -},{"uuid":79}],87:[function(require,module,exports){ -(function (global){(function (){ -'use strict'; - -var possibleNames = [ - 'BigInt64Array', - 'BigUint64Array', - 'Float32Array', - 'Float64Array', - 'Int16Array', - 'Int32Array', - 'Int8Array', - 'Uint16Array', - 'Uint32Array', - 'Uint8Array', - 'Uint8ClampedArray' -]; - -var g = typeof globalThis === 'undefined' ? global : globalThis; - -module.exports = function availableTypedArrays() { - var out = []; - for (var i = 0; i < possibleNames.length; i++) { - if (typeof g[possibleNames[i]] === 'function') { - out[out.length] = possibleNames[i]; - } - } - return out; -}; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],88:[function(require,module,exports){ -'use strict' - -exports.byteLength = byteLength -exports.toByteArray = toByteArray -exports.fromByteArray = fromByteArray - -var lookup = [] -var revLookup = [] -var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array - -var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -for (var i = 0, len = code.length; i < len; ++i) { - lookup[i] = code[i] - revLookup[code.charCodeAt(i)] = i -} - -// Support decoding URL-safe base64 strings, as Node.js does. -// See: https://en.wikipedia.org/wiki/Base64#URL_applications -revLookup['-'.charCodeAt(0)] = 62 -revLookup['_'.charCodeAt(0)] = 63 - -function getLens (b64) { - var len = b64.length - - if (len % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // Trim off extra bytes after placeholder bytes are found - // See: https://github.com/beatgammit/base64-js/issues/42 - var validLen = b64.indexOf('=') - if (validLen === -1) validLen = len - - var placeHoldersLen = validLen === len - ? 0 - : 4 - (validLen % 4) - - return [validLen, placeHoldersLen] -} - -// base64 is 4/3 + up to two characters of the original data -function byteLength (b64) { - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function _byteLength (b64, validLen, placeHoldersLen) { - return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen -} - -function toByteArray (b64) { - var tmp - var lens = getLens(b64) - var validLen = lens[0] - var placeHoldersLen = lens[1] - - var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) - - var curByte = 0 - - // if there are placeholders, only get up to the last complete 4 chars - var len = placeHoldersLen > 0 - ? validLen - 4 - : validLen - - var i - for (i = 0; i < len; i += 4) { - tmp = - (revLookup[b64.charCodeAt(i)] << 18) | - (revLookup[b64.charCodeAt(i + 1)] << 12) | - (revLookup[b64.charCodeAt(i + 2)] << 6) | - revLookup[b64.charCodeAt(i + 3)] - arr[curByte++] = (tmp >> 16) & 0xFF - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 2) { - tmp = - (revLookup[b64.charCodeAt(i)] << 2) | - (revLookup[b64.charCodeAt(i + 1)] >> 4) - arr[curByte++] = tmp & 0xFF - } - - if (placeHoldersLen === 1) { - tmp = - (revLookup[b64.charCodeAt(i)] << 10) | - (revLookup[b64.charCodeAt(i + 1)] << 4) | - (revLookup[b64.charCodeAt(i + 2)] >> 2) - arr[curByte++] = (tmp >> 8) & 0xFF - arr[curByte++] = tmp & 0xFF - } - - return arr -} - -function tripletToBase64 (num) { - return lookup[num >> 18 & 0x3F] + - lookup[num >> 12 & 0x3F] + - lookup[num >> 6 & 0x3F] + - lookup[num & 0x3F] -} - -function encodeChunk (uint8, start, end) { - var tmp - var output = [] - for (var i = start; i < end; i += 3) { - tmp = - ((uint8[i] << 16) & 0xFF0000) + - ((uint8[i + 1] << 8) & 0xFF00) + - (uint8[i + 2] & 0xFF) - output.push(tripletToBase64(tmp)) - } - return output.join('') -} - -function fromByteArray (uint8) { - var tmp - var len = uint8.length - var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes - var parts = [] - var maxChunkLength = 16383 // must be multiple of 3 - - // go through the array every three bytes, we'll deal with trailing stuff later - for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { - parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 1) { - tmp = uint8[len - 1] - parts.push( - lookup[tmp >> 2] + - lookup[(tmp << 4) & 0x3F] + - '==' - ) - } else if (extraBytes === 2) { - tmp = (uint8[len - 2] << 8) + uint8[len - 1] - parts.push( - lookup[tmp >> 10] + - lookup[(tmp >> 4) & 0x3F] + - lookup[(tmp << 2) & 0x3F] + - '=' - ) - } - - return parts.join('') -} - -},{}],89:[function(require,module,exports){ -(function (module, exports) { - 'use strict'; - - // Utils - function assert (val, msg) { - if (!val) throw new Error(msg || 'Assertion failed'); - } - - // Could use `inherits` module, but don't want to move from single file - // architecture yet. - function inherits (ctor, superCtor) { - ctor.super_ = superCtor; - var TempCtor = function () {}; - TempCtor.prototype = superCtor.prototype; - ctor.prototype = new TempCtor(); - ctor.prototype.constructor = ctor; - } - - // BN - - function BN (number, base, endian) { - if (BN.isBN(number)) { - return number; - } - - this.negative = 0; - this.words = null; - this.length = 0; - - // Reduction context - this.red = null; - - if (number !== null) { - if (base === 'le' || base === 'be') { - endian = base; - base = 10; - } - - this._init(number || 0, base || 10, endian || 'be'); - } - } - if (typeof module === 'object') { - module.exports = BN; - } else { - exports.BN = BN; - } - - BN.BN = BN; - BN.wordSize = 26; - - var Buffer; - try { - if (typeof window !== 'undefined' && typeof window.Buffer !== 'undefined') { - Buffer = window.Buffer; - } else { - Buffer = require('buffer').Buffer; - } - } catch (e) { - } - - BN.isBN = function isBN (num) { - if (num instanceof BN) { - return true; - } - - return num !== null && typeof num === 'object' && - num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); - }; - - BN.max = function max (left, right) { - if (left.cmp(right) > 0) return left; - return right; - }; - - BN.min = function min (left, right) { - if (left.cmp(right) < 0) return left; - return right; - }; - - BN.prototype._init = function init (number, base, endian) { - if (typeof number === 'number') { - return this._initNumber(number, base, endian); - } - - if (typeof number === 'object') { - return this._initArray(number, base, endian); - } - - if (base === 'hex') { - base = 16; - } - assert(base === (base | 0) && base >= 2 && base <= 36); - - number = number.toString().replace(/\s+/g, ''); - var start = 0; - if (number[0] === '-') { - start++; - this.negative = 1; - } - - if (start < number.length) { - if (base === 16) { - this._parseHex(number, start, endian); - } else { - this._parseBase(number, base, start); - if (endian === 'le') { - this._initArray(this.toArray(), base, endian); - } - } - } - }; - - BN.prototype._initNumber = function _initNumber (number, base, endian) { - if (number < 0) { - this.negative = 1; - number = -number; - } - if (number < 0x4000000) { - this.words = [number & 0x3ffffff]; - this.length = 1; - } else if (number < 0x10000000000000) { - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff - ]; - this.length = 2; - } else { - assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) - this.words = [ - number & 0x3ffffff, - (number / 0x4000000) & 0x3ffffff, - 1 - ]; - this.length = 3; - } - - if (endian !== 'le') return; - - // Reverse the bytes - this._initArray(this.toArray(), base, endian); - }; - - BN.prototype._initArray = function _initArray (number, base, endian) { - // Perhaps a Uint8Array - assert(typeof number.length === 'number'); - if (number.length <= 0) { - this.words = [0]; - this.length = 1; - return this; - } - - this.length = Math.ceil(number.length / 3); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - var j, w; - var off = 0; - if (endian === 'be') { - for (i = number.length - 1, j = 0; i >= 0; i -= 3) { - w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } else if (endian === 'le') { - for (i = 0, j = 0; i < number.length; i += 3) { - w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - } - return this._strip(); - }; - - function parseHex4Bits (string, index) { - var c = string.charCodeAt(index); - // '0' - '9' - if (c >= 48 && c <= 57) { - return c - 48; - // 'A' - 'F' - } else if (c >= 65 && c <= 70) { - return c - 55; - // 'a' - 'f' - } else if (c >= 97 && c <= 102) { - return c - 87; - } else { - assert(false, 'Invalid character in ' + string); - } - } - - function parseHexByte (string, lowerBound, index) { - var r = parseHex4Bits(string, index); - if (index - 1 >= lowerBound) { - r |= parseHex4Bits(string, index - 1) << 4; - } - return r; - } - - BN.prototype._parseHex = function _parseHex (number, start, endian) { - // Create possibly bigger array to ensure that it fits the number - this.length = Math.ceil((number.length - start) / 6); - this.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - this.words[i] = 0; - } - - // 24-bits chunks - var off = 0; - var j = 0; - - var w; - if (endian === 'be') { - for (i = number.length - 1; i >= start; i -= 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } else { - var parseLength = number.length - start; - for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { - w = parseHexByte(number, start, i) << off; - this.words[j] |= w & 0x3ffffff; - if (off >= 18) { - off -= 18; - j += 1; - this.words[j] |= w >>> 26; - } else { - off += 8; - } - } - } - - this._strip(); - }; - - function parseBase (str, start, end, mul) { - var r = 0; - var b = 0; - var len = Math.min(str.length, end); - for (var i = start; i < len; i++) { - var c = str.charCodeAt(i) - 48; - - r *= mul; - - // 'a' - if (c >= 49) { - b = c - 49 + 0xa; - - // 'A' - } else if (c >= 17) { - b = c - 17 + 0xa; - - // '0' - '9' - } else { - b = c; - } - assert(c >= 0 && b < mul, 'Invalid character'); - r += b; - } - return r; - } - - BN.prototype._parseBase = function _parseBase (number, base, start) { - // Initialize as zero - this.words = [0]; - this.length = 1; - - // Find length of limb in base - for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { - limbLen++; - } - limbLen--; - limbPow = (limbPow / base) | 0; - - var total = number.length - start; - var mod = total % limbLen; - var end = Math.min(total, total - mod) + start; - - var word = 0; - for (var i = start; i < end; i += limbLen) { - word = parseBase(number, i, i + limbLen, base); - - this.imuln(limbPow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - if (mod !== 0) { - var pow = 1; - word = parseBase(number, i, number.length, base); - - for (i = 0; i < mod; i++) { - pow *= base; - } - - this.imuln(pow); - if (this.words[0] + word < 0x4000000) { - this.words[0] += word; - } else { - this._iaddn(word); - } - } - - this._strip(); - }; - - BN.prototype.copy = function copy (dest) { - dest.words = new Array(this.length); - for (var i = 0; i < this.length; i++) { - dest.words[i] = this.words[i]; - } - dest.length = this.length; - dest.negative = this.negative; - dest.red = this.red; - }; - - function move (dest, src) { - dest.words = src.words; - dest.length = src.length; - dest.negative = src.negative; - dest.red = src.red; - } - - BN.prototype._move = function _move (dest) { - move(dest, this); - }; - - BN.prototype.clone = function clone () { - var r = new BN(null); - this.copy(r); - return r; - }; - - BN.prototype._expand = function _expand (size) { - while (this.length < size) { - this.words[this.length++] = 0; - } - return this; - }; - - // Remove leading `0` from `this` - BN.prototype._strip = function strip () { - while (this.length > 1 && this.words[this.length - 1] === 0) { - this.length--; - } - return this._normSign(); - }; - - BN.prototype._normSign = function _normSign () { - // -0 = 0 - if (this.length === 1 && this.words[0] === 0) { - this.negative = 0; - } - return this; - }; - - // Check Symbol.for because not everywhere where Symbol defined - // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility - if (typeof Symbol !== 'undefined' && typeof Symbol.for === 'function') { - try { - BN.prototype[Symbol.for('nodejs.util.inspect.custom')] = inspect; - } catch (e) { - BN.prototype.inspect = inspect; - } - } else { - BN.prototype.inspect = inspect; - } - - function inspect () { - return (this.red ? ''; - } - - /* - - var zeros = []; - var groupSizes = []; - var groupBases = []; - - var s = ''; - var i = -1; - while (++i < BN.wordSize) { - zeros[i] = s; - s += '0'; - } - groupSizes[0] = 0; - groupSizes[1] = 0; - groupBases[0] = 0; - groupBases[1] = 0; - var base = 2 - 1; - while (++base < 36 + 1) { - var groupSize = 0; - var groupBase = 1; - while (groupBase < (1 << BN.wordSize) / base) { - groupBase *= base; - groupSize += 1; - } - groupSizes[base] = groupSize; - groupBases[base] = groupBase; - } - - */ - - var zeros = [ - '', - '0', - '00', - '000', - '0000', - '00000', - '000000', - '0000000', - '00000000', - '000000000', - '0000000000', - '00000000000', - '000000000000', - '0000000000000', - '00000000000000', - '000000000000000', - '0000000000000000', - '00000000000000000', - '000000000000000000', - '0000000000000000000', - '00000000000000000000', - '000000000000000000000', - '0000000000000000000000', - '00000000000000000000000', - '000000000000000000000000', - '0000000000000000000000000' - ]; - - var groupSizes = [ - 0, 0, - 25, 16, 12, 11, 10, 9, 8, - 8, 7, 7, 7, 7, 6, 6, - 6, 6, 6, 6, 6, 5, 5, - 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5 - ]; - - var groupBases = [ - 0, 0, - 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, - 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, - 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, - 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, - 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 - ]; - - BN.prototype.toString = function toString (base, padding) { - base = base || 10; - padding = padding | 0 || 1; - - var out; - if (base === 16 || base === 'hex') { - out = ''; - var off = 0; - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = this.words[i]; - var word = (((w << off) | carry) & 0xffffff).toString(16); - carry = (w >>> (24 - off)) & 0xffffff; - if (carry !== 0 || i !== this.length - 1) { - out = zeros[6 - word.length] + word + out; - } else { - out = word + out; - } - off += 2; - if (off >= 26) { - off -= 26; - i--; - } - } - if (carry !== 0) { - out = carry.toString(16) + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - if (base === (base | 0) && base >= 2 && base <= 36) { - // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); - var groupSize = groupSizes[base]; - // var groupBase = Math.pow(base, groupSize); - var groupBase = groupBases[base]; - out = ''; - var c = this.clone(); - c.negative = 0; - while (!c.isZero()) { - var r = c.modrn(groupBase).toString(base); - c = c.idivn(groupBase); - - if (!c.isZero()) { - out = zeros[groupSize - r.length] + r + out; - } else { - out = r + out; - } - } - if (this.isZero()) { - out = '0' + out; - } - while (out.length % padding !== 0) { - out = '0' + out; - } - if (this.negative !== 0) { - out = '-' + out; - } - return out; - } - - assert(false, 'Base should be between 2 and 36'); - }; - - BN.prototype.toNumber = function toNumber () { - var ret = this.words[0]; - if (this.length === 2) { - ret += this.words[1] * 0x4000000; - } else if (this.length === 3 && this.words[2] === 0x01) { - // NOTE: at this stage it is known that the top bit is set - ret += 0x10000000000000 + (this.words[1] * 0x4000000); - } else if (this.length > 2) { - assert(false, 'Number can only safely store up to 53 bits'); - } - return (this.negative !== 0) ? -ret : ret; - }; - - BN.prototype.toJSON = function toJSON () { - return this.toString(16, 2); - }; - - if (Buffer) { - BN.prototype.toBuffer = function toBuffer (endian, length) { - return this.toArrayLike(Buffer, endian, length); - }; - } - - BN.prototype.toArray = function toArray (endian, length) { - return this.toArrayLike(Array, endian, length); - }; - - var allocate = function allocate (ArrayType, size) { - if (ArrayType.allocUnsafe) { - return ArrayType.allocUnsafe(size); - } - return new ArrayType(size); - }; - - BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { - this._strip(); - - var byteLength = this.byteLength(); - var reqLength = length || Math.max(1, byteLength); - assert(byteLength <= reqLength, 'byte array longer than desired length'); - assert(reqLength > 0, 'Requested array length <= 0'); - - var res = allocate(ArrayType, reqLength); - var postfix = endian === 'le' ? 'LE' : 'BE'; - this['_toArrayLike' + postfix](res, byteLength); - return res; - }; - - BN.prototype._toArrayLikeLE = function _toArrayLikeLE (res, byteLength) { - var position = 0; - var carry = 0; - - for (var i = 0, shift = 0; i < this.length; i++) { - var word = (this.words[i] << shift) | carry; - - res[position++] = word & 0xff; - if (position < res.length) { - res[position++] = (word >> 8) & 0xff; - } - if (position < res.length) { - res[position++] = (word >> 16) & 0xff; - } - - if (shift === 6) { - if (position < res.length) { - res[position++] = (word >> 24) & 0xff; - } - carry = 0; - shift = 0; - } else { - carry = word >>> 24; - shift += 2; - } - } - - if (position < res.length) { - res[position++] = carry; - - while (position < res.length) { - res[position++] = 0; - } - } - }; - - BN.prototype._toArrayLikeBE = function _toArrayLikeBE (res, byteLength) { - var position = res.length - 1; - var carry = 0; - - for (var i = 0, shift = 0; i < this.length; i++) { - var word = (this.words[i] << shift) | carry; - - res[position--] = word & 0xff; - if (position >= 0) { - res[position--] = (word >> 8) & 0xff; - } - if (position >= 0) { - res[position--] = (word >> 16) & 0xff; - } - - if (shift === 6) { - if (position >= 0) { - res[position--] = (word >> 24) & 0xff; - } - carry = 0; - shift = 0; - } else { - carry = word >>> 24; - shift += 2; - } - } - - if (position >= 0) { - res[position--] = carry; - - while (position >= 0) { - res[position--] = 0; - } - } - }; - - if (Math.clz32) { - BN.prototype._countBits = function _countBits (w) { - return 32 - Math.clz32(w); - }; - } else { - BN.prototype._countBits = function _countBits (w) { - var t = w; - var r = 0; - if (t >= 0x1000) { - r += 13; - t >>>= 13; - } - if (t >= 0x40) { - r += 7; - t >>>= 7; - } - if (t >= 0x8) { - r += 4; - t >>>= 4; - } - if (t >= 0x02) { - r += 2; - t >>>= 2; - } - return r + t; - }; - } - - BN.prototype._zeroBits = function _zeroBits (w) { - // Short-cut - if (w === 0) return 26; - - var t = w; - var r = 0; - if ((t & 0x1fff) === 0) { - r += 13; - t >>>= 13; - } - if ((t & 0x7f) === 0) { - r += 7; - t >>>= 7; - } - if ((t & 0xf) === 0) { - r += 4; - t >>>= 4; - } - if ((t & 0x3) === 0) { - r += 2; - t >>>= 2; - } - if ((t & 0x1) === 0) { - r++; - } - return r; - }; - - // Return number of used bits in a BN - BN.prototype.bitLength = function bitLength () { - var w = this.words[this.length - 1]; - var hi = this._countBits(w); - return (this.length - 1) * 26 + hi; - }; - - function toBitArray (num) { - var w = new Array(num.bitLength()); - - for (var bit = 0; bit < w.length; bit++) { - var off = (bit / 26) | 0; - var wbit = bit % 26; - - w[bit] = (num.words[off] >>> wbit) & 0x01; - } - - return w; - } - - // Number of trailing zero bits - BN.prototype.zeroBits = function zeroBits () { - if (this.isZero()) return 0; - - var r = 0; - for (var i = 0; i < this.length; i++) { - var b = this._zeroBits(this.words[i]); - r += b; - if (b !== 26) break; - } - return r; - }; - - BN.prototype.byteLength = function byteLength () { - return Math.ceil(this.bitLength() / 8); - }; - - BN.prototype.toTwos = function toTwos (width) { - if (this.negative !== 0) { - return this.abs().inotn(width).iaddn(1); - } - return this.clone(); - }; - - BN.prototype.fromTwos = function fromTwos (width) { - if (this.testn(width - 1)) { - return this.notn(width).iaddn(1).ineg(); - } - return this.clone(); - }; - - BN.prototype.isNeg = function isNeg () { - return this.negative !== 0; - }; - - // Return negative clone of `this` - BN.prototype.neg = function neg () { - return this.clone().ineg(); - }; - - BN.prototype.ineg = function ineg () { - if (!this.isZero()) { - this.negative ^= 1; - } - - return this; - }; - - // Or `num` with `this` in-place - BN.prototype.iuor = function iuor (num) { - while (this.length < num.length) { - this.words[this.length++] = 0; - } - - for (var i = 0; i < num.length; i++) { - this.words[i] = this.words[i] | num.words[i]; - } - - return this._strip(); - }; - - BN.prototype.ior = function ior (num) { - assert((this.negative | num.negative) === 0); - return this.iuor(num); - }; - - // Or `num` with `this` - BN.prototype.or = function or (num) { - if (this.length > num.length) return this.clone().ior(num); - return num.clone().ior(this); - }; - - BN.prototype.uor = function uor (num) { - if (this.length > num.length) return this.clone().iuor(num); - return num.clone().iuor(this); - }; - - // And `num` with `this` in-place - BN.prototype.iuand = function iuand (num) { - // b = min-length(num, this) - var b; - if (this.length > num.length) { - b = num; - } else { - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = this.words[i] & num.words[i]; - } - - this.length = b.length; - - return this._strip(); - }; - - BN.prototype.iand = function iand (num) { - assert((this.negative | num.negative) === 0); - return this.iuand(num); - }; - - // And `num` with `this` - BN.prototype.and = function and (num) { - if (this.length > num.length) return this.clone().iand(num); - return num.clone().iand(this); - }; - - BN.prototype.uand = function uand (num) { - if (this.length > num.length) return this.clone().iuand(num); - return num.clone().iuand(this); - }; - - // Xor `num` with `this` in-place - BN.prototype.iuxor = function iuxor (num) { - // a.length > b.length - var a; - var b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - for (var i = 0; i < b.length; i++) { - this.words[i] = a.words[i] ^ b.words[i]; - } - - if (this !== a) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = a.length; - - return this._strip(); - }; - - BN.prototype.ixor = function ixor (num) { - assert((this.negative | num.negative) === 0); - return this.iuxor(num); - }; - - // Xor `num` with `this` - BN.prototype.xor = function xor (num) { - if (this.length > num.length) return this.clone().ixor(num); - return num.clone().ixor(this); - }; - - BN.prototype.uxor = function uxor (num) { - if (this.length > num.length) return this.clone().iuxor(num); - return num.clone().iuxor(this); - }; - - // Not ``this`` with ``width`` bitwidth - BN.prototype.inotn = function inotn (width) { - assert(typeof width === 'number' && width >= 0); - - var bytesNeeded = Math.ceil(width / 26) | 0; - var bitsLeft = width % 26; - - // Extend the buffer with leading zeroes - this._expand(bytesNeeded); - - if (bitsLeft > 0) { - bytesNeeded--; - } - - // Handle complete words - for (var i = 0; i < bytesNeeded; i++) { - this.words[i] = ~this.words[i] & 0x3ffffff; - } - - // Handle the residue - if (bitsLeft > 0) { - this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); - } - - // And remove leading zeroes - return this._strip(); - }; - - BN.prototype.notn = function notn (width) { - return this.clone().inotn(width); - }; - - // Set `bit` of `this` - BN.prototype.setn = function setn (bit, val) { - assert(typeof bit === 'number' && bit >= 0); - - var off = (bit / 26) | 0; - var wbit = bit % 26; - - this._expand(off + 1); - - if (val) { - this.words[off] = this.words[off] | (1 << wbit); - } else { - this.words[off] = this.words[off] & ~(1 << wbit); - } - - return this._strip(); - }; - - // Add `num` to `this` in-place - BN.prototype.iadd = function iadd (num) { - var r; - - // negative + positive - if (this.negative !== 0 && num.negative === 0) { - this.negative = 0; - r = this.isub(num); - this.negative ^= 1; - return this._normSign(); - - // positive + negative - } else if (this.negative === 0 && num.negative !== 0) { - num.negative = 0; - r = this.isub(num); - num.negative = 1; - return r._normSign(); - } - - // a.length > b.length - var a, b; - if (this.length > num.length) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) + (b.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - this.words[i] = r & 0x3ffffff; - carry = r >>> 26; - } - - this.length = a.length; - if (carry !== 0) { - this.words[this.length] = carry; - this.length++; - // Copy the rest of the words - } else if (a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - return this; - }; - - // Add `num` to `this` - BN.prototype.add = function add (num) { - var res; - if (num.negative !== 0 && this.negative === 0) { - num.negative = 0; - res = this.sub(num); - num.negative ^= 1; - return res; - } else if (num.negative === 0 && this.negative !== 0) { - this.negative = 0; - res = num.sub(this); - this.negative = 1; - return res; - } - - if (this.length > num.length) return this.clone().iadd(num); - - return num.clone().iadd(this); - }; - - // Subtract `num` from `this` in-place - BN.prototype.isub = function isub (num) { - // this - (-num) = this + num - if (num.negative !== 0) { - num.negative = 0; - var r = this.iadd(num); - num.negative = 1; - return r._normSign(); - - // -this - num = -(this + num) - } else if (this.negative !== 0) { - this.negative = 0; - this.iadd(num); - this.negative = 1; - return this._normSign(); - } - - // At this point both numbers are positive - var cmp = this.cmp(num); - - // Optimization - zeroify - if (cmp === 0) { - this.negative = 0; - this.length = 1; - this.words[0] = 0; - return this; - } - - // a > b - var a, b; - if (cmp > 0) { - a = this; - b = num; - } else { - a = num; - b = this; - } - - var carry = 0; - for (var i = 0; i < b.length; i++) { - r = (a.words[i] | 0) - (b.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - for (; carry !== 0 && i < a.length; i++) { - r = (a.words[i] | 0) + carry; - carry = r >> 26; - this.words[i] = r & 0x3ffffff; - } - - // Copy rest of the words - if (carry === 0 && i < a.length && a !== this) { - for (; i < a.length; i++) { - this.words[i] = a.words[i]; - } - } - - this.length = Math.max(this.length, i); - - if (a !== this) { - this.negative = 1; - } - - return this._strip(); - }; - - // Subtract `num` from `this` - BN.prototype.sub = function sub (num) { - return this.clone().isub(num); - }; - - function smallMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - var len = (self.length + num.length) | 0; - out.length = len; - len = (len - 1) | 0; - - // Peel one iteration (compiler can't do it, because of code complexity) - var a = self.words[0] | 0; - var b = num.words[0] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - var carry = (r / 0x4000000) | 0; - out.words[0] = lo; - - for (var k = 1; k < len; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = carry >>> 26; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = (k - j) | 0; - a = self.words[i] | 0; - b = num.words[j] | 0; - r = a * b + rword; - ncarry += (r / 0x4000000) | 0; - rword = r & 0x3ffffff; - } - out.words[k] = rword | 0; - carry = ncarry | 0; - } - if (carry !== 0) { - out.words[k] = carry | 0; - } else { - out.length--; - } - - return out._strip(); - } - - // TODO(indutny): it may be reasonable to omit it for users who don't need - // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit - // multiplication (like elliptic secp256k1). - var comb10MulTo = function comb10MulTo (self, num, out) { - var a = self.words; - var b = num.words; - var o = out.words; - var c = 0; - var lo; - var mid; - var hi; - var a0 = a[0] | 0; - var al0 = a0 & 0x1fff; - var ah0 = a0 >>> 13; - var a1 = a[1] | 0; - var al1 = a1 & 0x1fff; - var ah1 = a1 >>> 13; - var a2 = a[2] | 0; - var al2 = a2 & 0x1fff; - var ah2 = a2 >>> 13; - var a3 = a[3] | 0; - var al3 = a3 & 0x1fff; - var ah3 = a3 >>> 13; - var a4 = a[4] | 0; - var al4 = a4 & 0x1fff; - var ah4 = a4 >>> 13; - var a5 = a[5] | 0; - var al5 = a5 & 0x1fff; - var ah5 = a5 >>> 13; - var a6 = a[6] | 0; - var al6 = a6 & 0x1fff; - var ah6 = a6 >>> 13; - var a7 = a[7] | 0; - var al7 = a7 & 0x1fff; - var ah7 = a7 >>> 13; - var a8 = a[8] | 0; - var al8 = a8 & 0x1fff; - var ah8 = a8 >>> 13; - var a9 = a[9] | 0; - var al9 = a9 & 0x1fff; - var ah9 = a9 >>> 13; - var b0 = b[0] | 0; - var bl0 = b0 & 0x1fff; - var bh0 = b0 >>> 13; - var b1 = b[1] | 0; - var bl1 = b1 & 0x1fff; - var bh1 = b1 >>> 13; - var b2 = b[2] | 0; - var bl2 = b2 & 0x1fff; - var bh2 = b2 >>> 13; - var b3 = b[3] | 0; - var bl3 = b3 & 0x1fff; - var bh3 = b3 >>> 13; - var b4 = b[4] | 0; - var bl4 = b4 & 0x1fff; - var bh4 = b4 >>> 13; - var b5 = b[5] | 0; - var bl5 = b5 & 0x1fff; - var bh5 = b5 >>> 13; - var b6 = b[6] | 0; - var bl6 = b6 & 0x1fff; - var bh6 = b6 >>> 13; - var b7 = b[7] | 0; - var bl7 = b7 & 0x1fff; - var bh7 = b7 >>> 13; - var b8 = b[8] | 0; - var bl8 = b8 & 0x1fff; - var bh8 = b8 >>> 13; - var b9 = b[9] | 0; - var bl9 = b9 & 0x1fff; - var bh9 = b9 >>> 13; - - out.negative = self.negative ^ num.negative; - out.length = 19; - /* k = 0 */ - lo = Math.imul(al0, bl0); - mid = Math.imul(al0, bh0); - mid = (mid + Math.imul(ah0, bl0)) | 0; - hi = Math.imul(ah0, bh0); - var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; - w0 &= 0x3ffffff; - /* k = 1 */ - lo = Math.imul(al1, bl0); - mid = Math.imul(al1, bh0); - mid = (mid + Math.imul(ah1, bl0)) | 0; - hi = Math.imul(ah1, bh0); - lo = (lo + Math.imul(al0, bl1)) | 0; - mid = (mid + Math.imul(al0, bh1)) | 0; - mid = (mid + Math.imul(ah0, bl1)) | 0; - hi = (hi + Math.imul(ah0, bh1)) | 0; - var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; - w1 &= 0x3ffffff; - /* k = 2 */ - lo = Math.imul(al2, bl0); - mid = Math.imul(al2, bh0); - mid = (mid + Math.imul(ah2, bl0)) | 0; - hi = Math.imul(ah2, bh0); - lo = (lo + Math.imul(al1, bl1)) | 0; - mid = (mid + Math.imul(al1, bh1)) | 0; - mid = (mid + Math.imul(ah1, bl1)) | 0; - hi = (hi + Math.imul(ah1, bh1)) | 0; - lo = (lo + Math.imul(al0, bl2)) | 0; - mid = (mid + Math.imul(al0, bh2)) | 0; - mid = (mid + Math.imul(ah0, bl2)) | 0; - hi = (hi + Math.imul(ah0, bh2)) | 0; - var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; - w2 &= 0x3ffffff; - /* k = 3 */ - lo = Math.imul(al3, bl0); - mid = Math.imul(al3, bh0); - mid = (mid + Math.imul(ah3, bl0)) | 0; - hi = Math.imul(ah3, bh0); - lo = (lo + Math.imul(al2, bl1)) | 0; - mid = (mid + Math.imul(al2, bh1)) | 0; - mid = (mid + Math.imul(ah2, bl1)) | 0; - hi = (hi + Math.imul(ah2, bh1)) | 0; - lo = (lo + Math.imul(al1, bl2)) | 0; - mid = (mid + Math.imul(al1, bh2)) | 0; - mid = (mid + Math.imul(ah1, bl2)) | 0; - hi = (hi + Math.imul(ah1, bh2)) | 0; - lo = (lo + Math.imul(al0, bl3)) | 0; - mid = (mid + Math.imul(al0, bh3)) | 0; - mid = (mid + Math.imul(ah0, bl3)) | 0; - hi = (hi + Math.imul(ah0, bh3)) | 0; - var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; - w3 &= 0x3ffffff; - /* k = 4 */ - lo = Math.imul(al4, bl0); - mid = Math.imul(al4, bh0); - mid = (mid + Math.imul(ah4, bl0)) | 0; - hi = Math.imul(ah4, bh0); - lo = (lo + Math.imul(al3, bl1)) | 0; - mid = (mid + Math.imul(al3, bh1)) | 0; - mid = (mid + Math.imul(ah3, bl1)) | 0; - hi = (hi + Math.imul(ah3, bh1)) | 0; - lo = (lo + Math.imul(al2, bl2)) | 0; - mid = (mid + Math.imul(al2, bh2)) | 0; - mid = (mid + Math.imul(ah2, bl2)) | 0; - hi = (hi + Math.imul(ah2, bh2)) | 0; - lo = (lo + Math.imul(al1, bl3)) | 0; - mid = (mid + Math.imul(al1, bh3)) | 0; - mid = (mid + Math.imul(ah1, bl3)) | 0; - hi = (hi + Math.imul(ah1, bh3)) | 0; - lo = (lo + Math.imul(al0, bl4)) | 0; - mid = (mid + Math.imul(al0, bh4)) | 0; - mid = (mid + Math.imul(ah0, bl4)) | 0; - hi = (hi + Math.imul(ah0, bh4)) | 0; - var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; - w4 &= 0x3ffffff; - /* k = 5 */ - lo = Math.imul(al5, bl0); - mid = Math.imul(al5, bh0); - mid = (mid + Math.imul(ah5, bl0)) | 0; - hi = Math.imul(ah5, bh0); - lo = (lo + Math.imul(al4, bl1)) | 0; - mid = (mid + Math.imul(al4, bh1)) | 0; - mid = (mid + Math.imul(ah4, bl1)) | 0; - hi = (hi + Math.imul(ah4, bh1)) | 0; - lo = (lo + Math.imul(al3, bl2)) | 0; - mid = (mid + Math.imul(al3, bh2)) | 0; - mid = (mid + Math.imul(ah3, bl2)) | 0; - hi = (hi + Math.imul(ah3, bh2)) | 0; - lo = (lo + Math.imul(al2, bl3)) | 0; - mid = (mid + Math.imul(al2, bh3)) | 0; - mid = (mid + Math.imul(ah2, bl3)) | 0; - hi = (hi + Math.imul(ah2, bh3)) | 0; - lo = (lo + Math.imul(al1, bl4)) | 0; - mid = (mid + Math.imul(al1, bh4)) | 0; - mid = (mid + Math.imul(ah1, bl4)) | 0; - hi = (hi + Math.imul(ah1, bh4)) | 0; - lo = (lo + Math.imul(al0, bl5)) | 0; - mid = (mid + Math.imul(al0, bh5)) | 0; - mid = (mid + Math.imul(ah0, bl5)) | 0; - hi = (hi + Math.imul(ah0, bh5)) | 0; - var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; - w5 &= 0x3ffffff; - /* k = 6 */ - lo = Math.imul(al6, bl0); - mid = Math.imul(al6, bh0); - mid = (mid + Math.imul(ah6, bl0)) | 0; - hi = Math.imul(ah6, bh0); - lo = (lo + Math.imul(al5, bl1)) | 0; - mid = (mid + Math.imul(al5, bh1)) | 0; - mid = (mid + Math.imul(ah5, bl1)) | 0; - hi = (hi + Math.imul(ah5, bh1)) | 0; - lo = (lo + Math.imul(al4, bl2)) | 0; - mid = (mid + Math.imul(al4, bh2)) | 0; - mid = (mid + Math.imul(ah4, bl2)) | 0; - hi = (hi + Math.imul(ah4, bh2)) | 0; - lo = (lo + Math.imul(al3, bl3)) | 0; - mid = (mid + Math.imul(al3, bh3)) | 0; - mid = (mid + Math.imul(ah3, bl3)) | 0; - hi = (hi + Math.imul(ah3, bh3)) | 0; - lo = (lo + Math.imul(al2, bl4)) | 0; - mid = (mid + Math.imul(al2, bh4)) | 0; - mid = (mid + Math.imul(ah2, bl4)) | 0; - hi = (hi + Math.imul(ah2, bh4)) | 0; - lo = (lo + Math.imul(al1, bl5)) | 0; - mid = (mid + Math.imul(al1, bh5)) | 0; - mid = (mid + Math.imul(ah1, bl5)) | 0; - hi = (hi + Math.imul(ah1, bh5)) | 0; - lo = (lo + Math.imul(al0, bl6)) | 0; - mid = (mid + Math.imul(al0, bh6)) | 0; - mid = (mid + Math.imul(ah0, bl6)) | 0; - hi = (hi + Math.imul(ah0, bh6)) | 0; - var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; - w6 &= 0x3ffffff; - /* k = 7 */ - lo = Math.imul(al7, bl0); - mid = Math.imul(al7, bh0); - mid = (mid + Math.imul(ah7, bl0)) | 0; - hi = Math.imul(ah7, bh0); - lo = (lo + Math.imul(al6, bl1)) | 0; - mid = (mid + Math.imul(al6, bh1)) | 0; - mid = (mid + Math.imul(ah6, bl1)) | 0; - hi = (hi + Math.imul(ah6, bh1)) | 0; - lo = (lo + Math.imul(al5, bl2)) | 0; - mid = (mid + Math.imul(al5, bh2)) | 0; - mid = (mid + Math.imul(ah5, bl2)) | 0; - hi = (hi + Math.imul(ah5, bh2)) | 0; - lo = (lo + Math.imul(al4, bl3)) | 0; - mid = (mid + Math.imul(al4, bh3)) | 0; - mid = (mid + Math.imul(ah4, bl3)) | 0; - hi = (hi + Math.imul(ah4, bh3)) | 0; - lo = (lo + Math.imul(al3, bl4)) | 0; - mid = (mid + Math.imul(al3, bh4)) | 0; - mid = (mid + Math.imul(ah3, bl4)) | 0; - hi = (hi + Math.imul(ah3, bh4)) | 0; - lo = (lo + Math.imul(al2, bl5)) | 0; - mid = (mid + Math.imul(al2, bh5)) | 0; - mid = (mid + Math.imul(ah2, bl5)) | 0; - hi = (hi + Math.imul(ah2, bh5)) | 0; - lo = (lo + Math.imul(al1, bl6)) | 0; - mid = (mid + Math.imul(al1, bh6)) | 0; - mid = (mid + Math.imul(ah1, bl6)) | 0; - hi = (hi + Math.imul(ah1, bh6)) | 0; - lo = (lo + Math.imul(al0, bl7)) | 0; - mid = (mid + Math.imul(al0, bh7)) | 0; - mid = (mid + Math.imul(ah0, bl7)) | 0; - hi = (hi + Math.imul(ah0, bh7)) | 0; - var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; - w7 &= 0x3ffffff; - /* k = 8 */ - lo = Math.imul(al8, bl0); - mid = Math.imul(al8, bh0); - mid = (mid + Math.imul(ah8, bl0)) | 0; - hi = Math.imul(ah8, bh0); - lo = (lo + Math.imul(al7, bl1)) | 0; - mid = (mid + Math.imul(al7, bh1)) | 0; - mid = (mid + Math.imul(ah7, bl1)) | 0; - hi = (hi + Math.imul(ah7, bh1)) | 0; - lo = (lo + Math.imul(al6, bl2)) | 0; - mid = (mid + Math.imul(al6, bh2)) | 0; - mid = (mid + Math.imul(ah6, bl2)) | 0; - hi = (hi + Math.imul(ah6, bh2)) | 0; - lo = (lo + Math.imul(al5, bl3)) | 0; - mid = (mid + Math.imul(al5, bh3)) | 0; - mid = (mid + Math.imul(ah5, bl3)) | 0; - hi = (hi + Math.imul(ah5, bh3)) | 0; - lo = (lo + Math.imul(al4, bl4)) | 0; - mid = (mid + Math.imul(al4, bh4)) | 0; - mid = (mid + Math.imul(ah4, bl4)) | 0; - hi = (hi + Math.imul(ah4, bh4)) | 0; - lo = (lo + Math.imul(al3, bl5)) | 0; - mid = (mid + Math.imul(al3, bh5)) | 0; - mid = (mid + Math.imul(ah3, bl5)) | 0; - hi = (hi + Math.imul(ah3, bh5)) | 0; - lo = (lo + Math.imul(al2, bl6)) | 0; - mid = (mid + Math.imul(al2, bh6)) | 0; - mid = (mid + Math.imul(ah2, bl6)) | 0; - hi = (hi + Math.imul(ah2, bh6)) | 0; - lo = (lo + Math.imul(al1, bl7)) | 0; - mid = (mid + Math.imul(al1, bh7)) | 0; - mid = (mid + Math.imul(ah1, bl7)) | 0; - hi = (hi + Math.imul(ah1, bh7)) | 0; - lo = (lo + Math.imul(al0, bl8)) | 0; - mid = (mid + Math.imul(al0, bh8)) | 0; - mid = (mid + Math.imul(ah0, bl8)) | 0; - hi = (hi + Math.imul(ah0, bh8)) | 0; - var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; - w8 &= 0x3ffffff; - /* k = 9 */ - lo = Math.imul(al9, bl0); - mid = Math.imul(al9, bh0); - mid = (mid + Math.imul(ah9, bl0)) | 0; - hi = Math.imul(ah9, bh0); - lo = (lo + Math.imul(al8, bl1)) | 0; - mid = (mid + Math.imul(al8, bh1)) | 0; - mid = (mid + Math.imul(ah8, bl1)) | 0; - hi = (hi + Math.imul(ah8, bh1)) | 0; - lo = (lo + Math.imul(al7, bl2)) | 0; - mid = (mid + Math.imul(al7, bh2)) | 0; - mid = (mid + Math.imul(ah7, bl2)) | 0; - hi = (hi + Math.imul(ah7, bh2)) | 0; - lo = (lo + Math.imul(al6, bl3)) | 0; - mid = (mid + Math.imul(al6, bh3)) | 0; - mid = (mid + Math.imul(ah6, bl3)) | 0; - hi = (hi + Math.imul(ah6, bh3)) | 0; - lo = (lo + Math.imul(al5, bl4)) | 0; - mid = (mid + Math.imul(al5, bh4)) | 0; - mid = (mid + Math.imul(ah5, bl4)) | 0; - hi = (hi + Math.imul(ah5, bh4)) | 0; - lo = (lo + Math.imul(al4, bl5)) | 0; - mid = (mid + Math.imul(al4, bh5)) | 0; - mid = (mid + Math.imul(ah4, bl5)) | 0; - hi = (hi + Math.imul(ah4, bh5)) | 0; - lo = (lo + Math.imul(al3, bl6)) | 0; - mid = (mid + Math.imul(al3, bh6)) | 0; - mid = (mid + Math.imul(ah3, bl6)) | 0; - hi = (hi + Math.imul(ah3, bh6)) | 0; - lo = (lo + Math.imul(al2, bl7)) | 0; - mid = (mid + Math.imul(al2, bh7)) | 0; - mid = (mid + Math.imul(ah2, bl7)) | 0; - hi = (hi + Math.imul(ah2, bh7)) | 0; - lo = (lo + Math.imul(al1, bl8)) | 0; - mid = (mid + Math.imul(al1, bh8)) | 0; - mid = (mid + Math.imul(ah1, bl8)) | 0; - hi = (hi + Math.imul(ah1, bh8)) | 0; - lo = (lo + Math.imul(al0, bl9)) | 0; - mid = (mid + Math.imul(al0, bh9)) | 0; - mid = (mid + Math.imul(ah0, bl9)) | 0; - hi = (hi + Math.imul(ah0, bh9)) | 0; - var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; - w9 &= 0x3ffffff; - /* k = 10 */ - lo = Math.imul(al9, bl1); - mid = Math.imul(al9, bh1); - mid = (mid + Math.imul(ah9, bl1)) | 0; - hi = Math.imul(ah9, bh1); - lo = (lo + Math.imul(al8, bl2)) | 0; - mid = (mid + Math.imul(al8, bh2)) | 0; - mid = (mid + Math.imul(ah8, bl2)) | 0; - hi = (hi + Math.imul(ah8, bh2)) | 0; - lo = (lo + Math.imul(al7, bl3)) | 0; - mid = (mid + Math.imul(al7, bh3)) | 0; - mid = (mid + Math.imul(ah7, bl3)) | 0; - hi = (hi + Math.imul(ah7, bh3)) | 0; - lo = (lo + Math.imul(al6, bl4)) | 0; - mid = (mid + Math.imul(al6, bh4)) | 0; - mid = (mid + Math.imul(ah6, bl4)) | 0; - hi = (hi + Math.imul(ah6, bh4)) | 0; - lo = (lo + Math.imul(al5, bl5)) | 0; - mid = (mid + Math.imul(al5, bh5)) | 0; - mid = (mid + Math.imul(ah5, bl5)) | 0; - hi = (hi + Math.imul(ah5, bh5)) | 0; - lo = (lo + Math.imul(al4, bl6)) | 0; - mid = (mid + Math.imul(al4, bh6)) | 0; - mid = (mid + Math.imul(ah4, bl6)) | 0; - hi = (hi + Math.imul(ah4, bh6)) | 0; - lo = (lo + Math.imul(al3, bl7)) | 0; - mid = (mid + Math.imul(al3, bh7)) | 0; - mid = (mid + Math.imul(ah3, bl7)) | 0; - hi = (hi + Math.imul(ah3, bh7)) | 0; - lo = (lo + Math.imul(al2, bl8)) | 0; - mid = (mid + Math.imul(al2, bh8)) | 0; - mid = (mid + Math.imul(ah2, bl8)) | 0; - hi = (hi + Math.imul(ah2, bh8)) | 0; - lo = (lo + Math.imul(al1, bl9)) | 0; - mid = (mid + Math.imul(al1, bh9)) | 0; - mid = (mid + Math.imul(ah1, bl9)) | 0; - hi = (hi + Math.imul(ah1, bh9)) | 0; - var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; - w10 &= 0x3ffffff; - /* k = 11 */ - lo = Math.imul(al9, bl2); - mid = Math.imul(al9, bh2); - mid = (mid + Math.imul(ah9, bl2)) | 0; - hi = Math.imul(ah9, bh2); - lo = (lo + Math.imul(al8, bl3)) | 0; - mid = (mid + Math.imul(al8, bh3)) | 0; - mid = (mid + Math.imul(ah8, bl3)) | 0; - hi = (hi + Math.imul(ah8, bh3)) | 0; - lo = (lo + Math.imul(al7, bl4)) | 0; - mid = (mid + Math.imul(al7, bh4)) | 0; - mid = (mid + Math.imul(ah7, bl4)) | 0; - hi = (hi + Math.imul(ah7, bh4)) | 0; - lo = (lo + Math.imul(al6, bl5)) | 0; - mid = (mid + Math.imul(al6, bh5)) | 0; - mid = (mid + Math.imul(ah6, bl5)) | 0; - hi = (hi + Math.imul(ah6, bh5)) | 0; - lo = (lo + Math.imul(al5, bl6)) | 0; - mid = (mid + Math.imul(al5, bh6)) | 0; - mid = (mid + Math.imul(ah5, bl6)) | 0; - hi = (hi + Math.imul(ah5, bh6)) | 0; - lo = (lo + Math.imul(al4, bl7)) | 0; - mid = (mid + Math.imul(al4, bh7)) | 0; - mid = (mid + Math.imul(ah4, bl7)) | 0; - hi = (hi + Math.imul(ah4, bh7)) | 0; - lo = (lo + Math.imul(al3, bl8)) | 0; - mid = (mid + Math.imul(al3, bh8)) | 0; - mid = (mid + Math.imul(ah3, bl8)) | 0; - hi = (hi + Math.imul(ah3, bh8)) | 0; - lo = (lo + Math.imul(al2, bl9)) | 0; - mid = (mid + Math.imul(al2, bh9)) | 0; - mid = (mid + Math.imul(ah2, bl9)) | 0; - hi = (hi + Math.imul(ah2, bh9)) | 0; - var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; - w11 &= 0x3ffffff; - /* k = 12 */ - lo = Math.imul(al9, bl3); - mid = Math.imul(al9, bh3); - mid = (mid + Math.imul(ah9, bl3)) | 0; - hi = Math.imul(ah9, bh3); - lo = (lo + Math.imul(al8, bl4)) | 0; - mid = (mid + Math.imul(al8, bh4)) | 0; - mid = (mid + Math.imul(ah8, bl4)) | 0; - hi = (hi + Math.imul(ah8, bh4)) | 0; - lo = (lo + Math.imul(al7, bl5)) | 0; - mid = (mid + Math.imul(al7, bh5)) | 0; - mid = (mid + Math.imul(ah7, bl5)) | 0; - hi = (hi + Math.imul(ah7, bh5)) | 0; - lo = (lo + Math.imul(al6, bl6)) | 0; - mid = (mid + Math.imul(al6, bh6)) | 0; - mid = (mid + Math.imul(ah6, bl6)) | 0; - hi = (hi + Math.imul(ah6, bh6)) | 0; - lo = (lo + Math.imul(al5, bl7)) | 0; - mid = (mid + Math.imul(al5, bh7)) | 0; - mid = (mid + Math.imul(ah5, bl7)) | 0; - hi = (hi + Math.imul(ah5, bh7)) | 0; - lo = (lo + Math.imul(al4, bl8)) | 0; - mid = (mid + Math.imul(al4, bh8)) | 0; - mid = (mid + Math.imul(ah4, bl8)) | 0; - hi = (hi + Math.imul(ah4, bh8)) | 0; - lo = (lo + Math.imul(al3, bl9)) | 0; - mid = (mid + Math.imul(al3, bh9)) | 0; - mid = (mid + Math.imul(ah3, bl9)) | 0; - hi = (hi + Math.imul(ah3, bh9)) | 0; - var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; - w12 &= 0x3ffffff; - /* k = 13 */ - lo = Math.imul(al9, bl4); - mid = Math.imul(al9, bh4); - mid = (mid + Math.imul(ah9, bl4)) | 0; - hi = Math.imul(ah9, bh4); - lo = (lo + Math.imul(al8, bl5)) | 0; - mid = (mid + Math.imul(al8, bh5)) | 0; - mid = (mid + Math.imul(ah8, bl5)) | 0; - hi = (hi + Math.imul(ah8, bh5)) | 0; - lo = (lo + Math.imul(al7, bl6)) | 0; - mid = (mid + Math.imul(al7, bh6)) | 0; - mid = (mid + Math.imul(ah7, bl6)) | 0; - hi = (hi + Math.imul(ah7, bh6)) | 0; - lo = (lo + Math.imul(al6, bl7)) | 0; - mid = (mid + Math.imul(al6, bh7)) | 0; - mid = (mid + Math.imul(ah6, bl7)) | 0; - hi = (hi + Math.imul(ah6, bh7)) | 0; - lo = (lo + Math.imul(al5, bl8)) | 0; - mid = (mid + Math.imul(al5, bh8)) | 0; - mid = (mid + Math.imul(ah5, bl8)) | 0; - hi = (hi + Math.imul(ah5, bh8)) | 0; - lo = (lo + Math.imul(al4, bl9)) | 0; - mid = (mid + Math.imul(al4, bh9)) | 0; - mid = (mid + Math.imul(ah4, bl9)) | 0; - hi = (hi + Math.imul(ah4, bh9)) | 0; - var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; - w13 &= 0x3ffffff; - /* k = 14 */ - lo = Math.imul(al9, bl5); - mid = Math.imul(al9, bh5); - mid = (mid + Math.imul(ah9, bl5)) | 0; - hi = Math.imul(ah9, bh5); - lo = (lo + Math.imul(al8, bl6)) | 0; - mid = (mid + Math.imul(al8, bh6)) | 0; - mid = (mid + Math.imul(ah8, bl6)) | 0; - hi = (hi + Math.imul(ah8, bh6)) | 0; - lo = (lo + Math.imul(al7, bl7)) | 0; - mid = (mid + Math.imul(al7, bh7)) | 0; - mid = (mid + Math.imul(ah7, bl7)) | 0; - hi = (hi + Math.imul(ah7, bh7)) | 0; - lo = (lo + Math.imul(al6, bl8)) | 0; - mid = (mid + Math.imul(al6, bh8)) | 0; - mid = (mid + Math.imul(ah6, bl8)) | 0; - hi = (hi + Math.imul(ah6, bh8)) | 0; - lo = (lo + Math.imul(al5, bl9)) | 0; - mid = (mid + Math.imul(al5, bh9)) | 0; - mid = (mid + Math.imul(ah5, bl9)) | 0; - hi = (hi + Math.imul(ah5, bh9)) | 0; - var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; - w14 &= 0x3ffffff; - /* k = 15 */ - lo = Math.imul(al9, bl6); - mid = Math.imul(al9, bh6); - mid = (mid + Math.imul(ah9, bl6)) | 0; - hi = Math.imul(ah9, bh6); - lo = (lo + Math.imul(al8, bl7)) | 0; - mid = (mid + Math.imul(al8, bh7)) | 0; - mid = (mid + Math.imul(ah8, bl7)) | 0; - hi = (hi + Math.imul(ah8, bh7)) | 0; - lo = (lo + Math.imul(al7, bl8)) | 0; - mid = (mid + Math.imul(al7, bh8)) | 0; - mid = (mid + Math.imul(ah7, bl8)) | 0; - hi = (hi + Math.imul(ah7, bh8)) | 0; - lo = (lo + Math.imul(al6, bl9)) | 0; - mid = (mid + Math.imul(al6, bh9)) | 0; - mid = (mid + Math.imul(ah6, bl9)) | 0; - hi = (hi + Math.imul(ah6, bh9)) | 0; - var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; - w15 &= 0x3ffffff; - /* k = 16 */ - lo = Math.imul(al9, bl7); - mid = Math.imul(al9, bh7); - mid = (mid + Math.imul(ah9, bl7)) | 0; - hi = Math.imul(ah9, bh7); - lo = (lo + Math.imul(al8, bl8)) | 0; - mid = (mid + Math.imul(al8, bh8)) | 0; - mid = (mid + Math.imul(ah8, bl8)) | 0; - hi = (hi + Math.imul(ah8, bh8)) | 0; - lo = (lo + Math.imul(al7, bl9)) | 0; - mid = (mid + Math.imul(al7, bh9)) | 0; - mid = (mid + Math.imul(ah7, bl9)) | 0; - hi = (hi + Math.imul(ah7, bh9)) | 0; - var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; - w16 &= 0x3ffffff; - /* k = 17 */ - lo = Math.imul(al9, bl8); - mid = Math.imul(al9, bh8); - mid = (mid + Math.imul(ah9, bl8)) | 0; - hi = Math.imul(ah9, bh8); - lo = (lo + Math.imul(al8, bl9)) | 0; - mid = (mid + Math.imul(al8, bh9)) | 0; - mid = (mid + Math.imul(ah8, bl9)) | 0; - hi = (hi + Math.imul(ah8, bh9)) | 0; - var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; - w17 &= 0x3ffffff; - /* k = 18 */ - lo = Math.imul(al9, bl9); - mid = Math.imul(al9, bh9); - mid = (mid + Math.imul(ah9, bl9)) | 0; - hi = Math.imul(ah9, bh9); - var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; - c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; - w18 &= 0x3ffffff; - o[0] = w0; - o[1] = w1; - o[2] = w2; - o[3] = w3; - o[4] = w4; - o[5] = w5; - o[6] = w6; - o[7] = w7; - o[8] = w8; - o[9] = w9; - o[10] = w10; - o[11] = w11; - o[12] = w12; - o[13] = w13; - o[14] = w14; - o[15] = w15; - o[16] = w16; - o[17] = w17; - o[18] = w18; - if (c !== 0) { - o[19] = c; - out.length++; - } - return out; - }; - - // Polyfill comb - if (!Math.imul) { - comb10MulTo = smallMulTo; - } - - function bigMulTo (self, num, out) { - out.negative = num.negative ^ self.negative; - out.length = self.length + num.length; - - var carry = 0; - var hncarry = 0; - for (var k = 0; k < out.length - 1; k++) { - // Sum all words with the same `i + j = k` and accumulate `ncarry`, - // note that ncarry could be >= 0x3ffffff - var ncarry = hncarry; - hncarry = 0; - var rword = carry & 0x3ffffff; - var maxJ = Math.min(k, num.length - 1); - for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { - var i = k - j; - var a = self.words[i] | 0; - var b = num.words[j] | 0; - var r = a * b; - - var lo = r & 0x3ffffff; - ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; - lo = (lo + rword) | 0; - rword = lo & 0x3ffffff; - ncarry = (ncarry + (lo >>> 26)) | 0; - - hncarry += ncarry >>> 26; - ncarry &= 0x3ffffff; - } - out.words[k] = rword; - carry = ncarry; - ncarry = hncarry; - } - if (carry !== 0) { - out.words[k] = carry; - } else { - out.length--; - } - - return out._strip(); - } - - function jumboMulTo (self, num, out) { - // Temporary disable, see https://github.com/indutny/bn.js/issues/211 - // var fftm = new FFTM(); - // return fftm.mulp(self, num, out); - return bigMulTo(self, num, out); - } - - BN.prototype.mulTo = function mulTo (num, out) { - var res; - var len = this.length + num.length; - if (this.length === 10 && num.length === 10) { - res = comb10MulTo(this, num, out); - } else if (len < 63) { - res = smallMulTo(this, num, out); - } else if (len < 1024) { - res = bigMulTo(this, num, out); - } else { - res = jumboMulTo(this, num, out); - } - - return res; - }; - - // Cooley-Tukey algorithm for FFT - // slightly revisited to rely on looping instead of recursion - - function FFTM (x, y) { - this.x = x; - this.y = y; - } - - FFTM.prototype.makeRBT = function makeRBT (N) { - var t = new Array(N); - var l = BN.prototype._countBits(N) - 1; - for (var i = 0; i < N; i++) { - t[i] = this.revBin(i, l, N); - } - - return t; - }; - - // Returns binary-reversed representation of `x` - FFTM.prototype.revBin = function revBin (x, l, N) { - if (x === 0 || x === N - 1) return x; - - var rb = 0; - for (var i = 0; i < l; i++) { - rb |= (x & 1) << (l - i - 1); - x >>= 1; - } - - return rb; - }; - - // Performs "tweedling" phase, therefore 'emulating' - // behaviour of the recursive algorithm - FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { - for (var i = 0; i < N; i++) { - rtws[i] = rws[rbt[i]]; - itws[i] = iws[rbt[i]]; - } - }; - - FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { - this.permute(rbt, rws, iws, rtws, itws, N); - - for (var s = 1; s < N; s <<= 1) { - var l = s << 1; - - var rtwdf = Math.cos(2 * Math.PI / l); - var itwdf = Math.sin(2 * Math.PI / l); - - for (var p = 0; p < N; p += l) { - var rtwdf_ = rtwdf; - var itwdf_ = itwdf; - - for (var j = 0; j < s; j++) { - var re = rtws[p + j]; - var ie = itws[p + j]; - - var ro = rtws[p + j + s]; - var io = itws[p + j + s]; - - var rx = rtwdf_ * ro - itwdf_ * io; - - io = rtwdf_ * io + itwdf_ * ro; - ro = rx; - - rtws[p + j] = re + ro; - itws[p + j] = ie + io; - - rtws[p + j + s] = re - ro; - itws[p + j + s] = ie - io; - - /* jshint maxdepth : false */ - if (j !== l) { - rx = rtwdf * rtwdf_ - itwdf * itwdf_; - - itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; - rtwdf_ = rx; - } - } - } - } - }; - - FFTM.prototype.guessLen13b = function guessLen13b (n, m) { - var N = Math.max(m, n) | 1; - var odd = N & 1; - var i = 0; - for (N = N / 2 | 0; N; N = N >>> 1) { - i++; - } - - return 1 << i + 1 + odd; - }; - - FFTM.prototype.conjugate = function conjugate (rws, iws, N) { - if (N <= 1) return; - - for (var i = 0; i < N / 2; i++) { - var t = rws[i]; - - rws[i] = rws[N - i - 1]; - rws[N - i - 1] = t; - - t = iws[i]; - - iws[i] = -iws[N - i - 1]; - iws[N - i - 1] = -t; - } - }; - - FFTM.prototype.normalize13b = function normalize13b (ws, N) { - var carry = 0; - for (var i = 0; i < N / 2; i++) { - var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + - Math.round(ws[2 * i] / N) + - carry; - - ws[i] = w & 0x3ffffff; - - if (w < 0x4000000) { - carry = 0; - } else { - carry = w / 0x4000000 | 0; - } - } - - return ws; - }; - - FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { - var carry = 0; - for (var i = 0; i < len; i++) { - carry = carry + (ws[i] | 0); - - rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; - rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; - } - - // Pad with zeroes - for (i = 2 * len; i < N; ++i) { - rws[i] = 0; - } - - assert(carry === 0); - assert((carry & ~0x1fff) === 0); - }; - - FFTM.prototype.stub = function stub (N) { - var ph = new Array(N); - for (var i = 0; i < N; i++) { - ph[i] = 0; - } - - return ph; - }; - - FFTM.prototype.mulp = function mulp (x, y, out) { - var N = 2 * this.guessLen13b(x.length, y.length); - - var rbt = this.makeRBT(N); - - var _ = this.stub(N); - - var rws = new Array(N); - var rwst = new Array(N); - var iwst = new Array(N); - - var nrws = new Array(N); - var nrwst = new Array(N); - var niwst = new Array(N); - - var rmws = out.words; - rmws.length = N; - - this.convert13b(x.words, x.length, rws, N); - this.convert13b(y.words, y.length, nrws, N); - - this.transform(rws, _, rwst, iwst, N, rbt); - this.transform(nrws, _, nrwst, niwst, N, rbt); - - for (var i = 0; i < N; i++) { - var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; - iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; - rwst[i] = rx; - } - - this.conjugate(rwst, iwst, N); - this.transform(rwst, iwst, rmws, _, N, rbt); - this.conjugate(rmws, _, N); - this.normalize13b(rmws, N); - - out.negative = x.negative ^ y.negative; - out.length = x.length + y.length; - return out._strip(); - }; - - // Multiply `this` by `num` - BN.prototype.mul = function mul (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return this.mulTo(num, out); - }; - - // Multiply employing FFT - BN.prototype.mulf = function mulf (num) { - var out = new BN(null); - out.words = new Array(this.length + num.length); - return jumboMulTo(this, num, out); - }; - - // In-place Multiplication - BN.prototype.imul = function imul (num) { - return this.clone().mulTo(num, this); - }; - - BN.prototype.imuln = function imuln (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(typeof num === 'number'); - assert(num < 0x4000000); - - // Carry - var carry = 0; - for (var i = 0; i < this.length; i++) { - var w = (this.words[i] | 0) * num; - var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); - carry >>= 26; - carry += (w / 0x4000000) | 0; - // NOTE: lo is 27bit maximum - carry += lo >>> 26; - this.words[i] = lo & 0x3ffffff; - } - - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - - return isNegNum ? this.ineg() : this; - }; - - BN.prototype.muln = function muln (num) { - return this.clone().imuln(num); - }; - - // `this` * `this` - BN.prototype.sqr = function sqr () { - return this.mul(this); - }; - - // `this` * `this` in-place - BN.prototype.isqr = function isqr () { - return this.imul(this.clone()); - }; - - // Math.pow(`this`, `num`) - BN.prototype.pow = function pow (num) { - var w = toBitArray(num); - if (w.length === 0) return new BN(1); - - // Skip leading zeroes - var res = this; - for (var i = 0; i < w.length; i++, res = res.sqr()) { - if (w[i] !== 0) break; - } - - if (++i < w.length) { - for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { - if (w[i] === 0) continue; - - res = res.mul(q); - } - } - - return res; - }; - - // Shift-left in-place - BN.prototype.iushln = function iushln (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); - var i; - - if (r !== 0) { - var carry = 0; - - for (i = 0; i < this.length; i++) { - var newCarry = this.words[i] & carryMask; - var c = ((this.words[i] | 0) - newCarry) << r; - this.words[i] = c | carry; - carry = newCarry >>> (26 - r); - } - - if (carry) { - this.words[i] = carry; - this.length++; - } - } - - if (s !== 0) { - for (i = this.length - 1; i >= 0; i--) { - this.words[i + s] = this.words[i]; - } - - for (i = 0; i < s; i++) { - this.words[i] = 0; - } - - this.length += s; - } - - return this._strip(); - }; - - BN.prototype.ishln = function ishln (bits) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushln(bits); - }; - - // Shift-right in-place - // NOTE: `hint` is a lowest bit before trailing zeroes - // NOTE: if `extended` is present - it will be filled with destroyed bits - BN.prototype.iushrn = function iushrn (bits, hint, extended) { - assert(typeof bits === 'number' && bits >= 0); - var h; - if (hint) { - h = (hint - (hint % 26)) / 26; - } else { - h = 0; - } - - var r = bits % 26; - var s = Math.min((bits - r) / 26, this.length); - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - var maskedWords = extended; - - h -= s; - h = Math.max(0, h); - - // Extended mode, copy masked part - if (maskedWords) { - for (var i = 0; i < s; i++) { - maskedWords.words[i] = this.words[i]; - } - maskedWords.length = s; - } - - if (s === 0) { - // No-op, we should not move anything at all - } else if (this.length > s) { - this.length -= s; - for (i = 0; i < this.length; i++) { - this.words[i] = this.words[i + s]; - } - } else { - this.words[0] = 0; - this.length = 1; - } - - var carry = 0; - for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { - var word = this.words[i] | 0; - this.words[i] = (carry << (26 - r)) | (word >>> r); - carry = word & mask; - } - - // Push carried bits as a mask - if (maskedWords && carry !== 0) { - maskedWords.words[maskedWords.length++] = carry; - } - - if (this.length === 0) { - this.words[0] = 0; - this.length = 1; - } - - return this._strip(); - }; - - BN.prototype.ishrn = function ishrn (bits, hint, extended) { - // TODO(indutny): implement me - assert(this.negative === 0); - return this.iushrn(bits, hint, extended); - }; - - // Shift-left - BN.prototype.shln = function shln (bits) { - return this.clone().ishln(bits); - }; - - BN.prototype.ushln = function ushln (bits) { - return this.clone().iushln(bits); - }; - - // Shift-right - BN.prototype.shrn = function shrn (bits) { - return this.clone().ishrn(bits); - }; - - BN.prototype.ushrn = function ushrn (bits) { - return this.clone().iushrn(bits); - }; - - // Test if n bit is set - BN.prototype.testn = function testn (bit) { - assert(typeof bit === 'number' && bit >= 0); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) return false; - - // Check bit and return - var w = this.words[s]; - - return !!(w & q); - }; - - // Return only lowers bits of number (in-place) - BN.prototype.imaskn = function imaskn (bits) { - assert(typeof bits === 'number' && bits >= 0); - var r = bits % 26; - var s = (bits - r) / 26; - - assert(this.negative === 0, 'imaskn works only with positive numbers'); - - if (this.length <= s) { - return this; - } - - if (r !== 0) { - s++; - } - this.length = Math.min(s, this.length); - - if (r !== 0) { - var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); - this.words[this.length - 1] &= mask; - } - - return this._strip(); - }; - - // Return only lowers bits of number - BN.prototype.maskn = function maskn (bits) { - return this.clone().imaskn(bits); - }; - - // Add plain number `num` to `this` - BN.prototype.iaddn = function iaddn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.isubn(-num); - - // Possible sign change - if (this.negative !== 0) { - if (this.length === 1 && (this.words[0] | 0) <= num) { - this.words[0] = num - (this.words[0] | 0); - this.negative = 0; - return this; - } - - this.negative = 0; - this.isubn(num); - this.negative = 1; - return this; - } - - // Add without checks - return this._iaddn(num); - }; - - BN.prototype._iaddn = function _iaddn (num) { - this.words[0] += num; - - // Carry - for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { - this.words[i] -= 0x4000000; - if (i === this.length - 1) { - this.words[i + 1] = 1; - } else { - this.words[i + 1]++; - } - } - this.length = Math.max(this.length, i + 1); - - return this; - }; - - // Subtract plain number `num` from `this` - BN.prototype.isubn = function isubn (num) { - assert(typeof num === 'number'); - assert(num < 0x4000000); - if (num < 0) return this.iaddn(-num); - - if (this.negative !== 0) { - this.negative = 0; - this.iaddn(num); - this.negative = 1; - return this; - } - - this.words[0] -= num; - - if (this.length === 1 && this.words[0] < 0) { - this.words[0] = -this.words[0]; - this.negative = 1; - } else { - // Carry - for (var i = 0; i < this.length && this.words[i] < 0; i++) { - this.words[i] += 0x4000000; - this.words[i + 1] -= 1; - } - } - - return this._strip(); - }; - - BN.prototype.addn = function addn (num) { - return this.clone().iaddn(num); - }; - - BN.prototype.subn = function subn (num) { - return this.clone().isubn(num); - }; - - BN.prototype.iabs = function iabs () { - this.negative = 0; - - return this; - }; - - BN.prototype.abs = function abs () { - return this.clone().iabs(); - }; - - BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { - var len = num.length + shift; - var i; - - this._expand(len); - - var w; - var carry = 0; - for (i = 0; i < num.length; i++) { - w = (this.words[i + shift] | 0) + carry; - var right = (num.words[i] | 0) * mul; - w -= right & 0x3ffffff; - carry = (w >> 26) - ((right / 0x4000000) | 0); - this.words[i + shift] = w & 0x3ffffff; - } - for (; i < this.length - shift; i++) { - w = (this.words[i + shift] | 0) + carry; - carry = w >> 26; - this.words[i + shift] = w & 0x3ffffff; - } - - if (carry === 0) return this._strip(); - - // Subtraction overflow - assert(carry === -1); - carry = 0; - for (i = 0; i < this.length; i++) { - w = -(this.words[i] | 0) + carry; - carry = w >> 26; - this.words[i] = w & 0x3ffffff; - } - this.negative = 1; - - return this._strip(); - }; - - BN.prototype._wordDiv = function _wordDiv (num, mode) { - var shift = this.length - num.length; - - var a = this.clone(); - var b = num; - - // Normalize - var bhi = b.words[b.length - 1] | 0; - var bhiBits = this._countBits(bhi); - shift = 26 - bhiBits; - if (shift !== 0) { - b = b.ushln(shift); - a.iushln(shift); - bhi = b.words[b.length - 1] | 0; - } - - // Initialize quotient - var m = a.length - b.length; - var q; - - if (mode !== 'mod') { - q = new BN(null); - q.length = m + 1; - q.words = new Array(q.length); - for (var i = 0; i < q.length; i++) { - q.words[i] = 0; - } - } - - var diff = a.clone()._ishlnsubmul(b, 1, m); - if (diff.negative === 0) { - a = diff; - if (q) { - q.words[m] = 1; - } - } - - for (var j = m - 1; j >= 0; j--) { - var qj = (a.words[b.length + j] | 0) * 0x4000000 + - (a.words[b.length + j - 1] | 0); - - // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max - // (0x7ffffff) - qj = Math.min((qj / bhi) | 0, 0x3ffffff); - - a._ishlnsubmul(b, qj, j); - while (a.negative !== 0) { - qj--; - a.negative = 0; - a._ishlnsubmul(b, 1, j); - if (!a.isZero()) { - a.negative ^= 1; - } - } - if (q) { - q.words[j] = qj; - } - } - if (q) { - q._strip(); - } - a._strip(); - - // Denormalize - if (mode !== 'div' && shift !== 0) { - a.iushrn(shift); - } - - return { - div: q || null, - mod: a - }; - }; - - // NOTE: 1) `mode` can be set to `mod` to request mod only, - // to `div` to request div only, or be absent to - // request both div & mod - // 2) `positive` is true if unsigned mod is requested - BN.prototype.divmod = function divmod (num, mode, positive) { - assert(!num.isZero()); - - if (this.isZero()) { - return { - div: new BN(0), - mod: new BN(0) - }; - } - - var div, mod, res; - if (this.negative !== 0 && num.negative === 0) { - res = this.neg().divmod(num, mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.iadd(num); - } - } - - return { - div: div, - mod: mod - }; - } - - if (this.negative === 0 && num.negative !== 0) { - res = this.divmod(num.neg(), mode); - - if (mode !== 'mod') { - div = res.div.neg(); - } - - return { - div: div, - mod: res.mod - }; - } - - if ((this.negative & num.negative) !== 0) { - res = this.neg().divmod(num.neg(), mode); - - if (mode !== 'div') { - mod = res.mod.neg(); - if (positive && mod.negative !== 0) { - mod.isub(num); - } - } - - return { - div: res.div, - mod: mod - }; - } - - // Both numbers are positive at this point - - // Strip both numbers to approximate shift value - if (num.length > this.length || this.cmp(num) < 0) { - return { - div: new BN(0), - mod: this - }; - } - - // Very short reduction - if (num.length === 1) { - if (mode === 'div') { - return { - div: this.divn(num.words[0]), - mod: null - }; - } - - if (mode === 'mod') { - return { - div: null, - mod: new BN(this.modrn(num.words[0])) - }; - } - - return { - div: this.divn(num.words[0]), - mod: new BN(this.modrn(num.words[0])) - }; - } - - return this._wordDiv(num, mode); - }; - - // Find `this` / `num` - BN.prototype.div = function div (num) { - return this.divmod(num, 'div', false).div; - }; - - // Find `this` % `num` - BN.prototype.mod = function mod (num) { - return this.divmod(num, 'mod', false).mod; - }; - - BN.prototype.umod = function umod (num) { - return this.divmod(num, 'mod', true).mod; - }; - - // Find Round(`this` / `num`) - BN.prototype.divRound = function divRound (num) { - var dm = this.divmod(num); - - // Fast case - exact division - if (dm.mod.isZero()) return dm.div; - - var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; - - var half = num.ushrn(1); - var r2 = num.andln(1); - var cmp = mod.cmp(half); - - // Round down - if (cmp < 0 || (r2 === 1 && cmp === 0)) return dm.div; - - // Round up - return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); - }; - - BN.prototype.modrn = function modrn (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(num <= 0x3ffffff); - var p = (1 << 26) % num; - - var acc = 0; - for (var i = this.length - 1; i >= 0; i--) { - acc = (p * acc + (this.words[i] | 0)) % num; - } - - return isNegNum ? -acc : acc; - }; - - // WARNING: DEPRECATED - BN.prototype.modn = function modn (num) { - return this.modrn(num); - }; - - // In-place division by number - BN.prototype.idivn = function idivn (num) { - var isNegNum = num < 0; - if (isNegNum) num = -num; - - assert(num <= 0x3ffffff); - - var carry = 0; - for (var i = this.length - 1; i >= 0; i--) { - var w = (this.words[i] | 0) + carry * 0x4000000; - this.words[i] = (w / num) | 0; - carry = w % num; - } - - this._strip(); - return isNegNum ? this.ineg() : this; - }; - - BN.prototype.divn = function divn (num) { - return this.clone().idivn(num); - }; - - BN.prototype.egcd = function egcd (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var x = this; - var y = p.clone(); - - if (x.negative !== 0) { - x = x.umod(p); - } else { - x = x.clone(); - } - - // A * x + B * y = x - var A = new BN(1); - var B = new BN(0); - - // C * x + D * y = y - var C = new BN(0); - var D = new BN(1); - - var g = 0; - - while (x.isEven() && y.isEven()) { - x.iushrn(1); - y.iushrn(1); - ++g; - } - - var yp = y.clone(); - var xp = x.clone(); - - while (!x.isZero()) { - for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - x.iushrn(i); - while (i-- > 0) { - if (A.isOdd() || B.isOdd()) { - A.iadd(yp); - B.isub(xp); - } - - A.iushrn(1); - B.iushrn(1); - } - } - - for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - y.iushrn(j); - while (j-- > 0) { - if (C.isOdd() || D.isOdd()) { - C.iadd(yp); - D.isub(xp); - } - - C.iushrn(1); - D.iushrn(1); - } - } - - if (x.cmp(y) >= 0) { - x.isub(y); - A.isub(C); - B.isub(D); - } else { - y.isub(x); - C.isub(A); - D.isub(B); - } - } - - return { - a: C, - b: D, - gcd: y.iushln(g) - }; - }; - - // This is reduced incarnation of the binary EEA - // above, designated to invert members of the - // _prime_ fields F(p) at a maximal speed - BN.prototype._invmp = function _invmp (p) { - assert(p.negative === 0); - assert(!p.isZero()); - - var a = this; - var b = p.clone(); - - if (a.negative !== 0) { - a = a.umod(p); - } else { - a = a.clone(); - } - - var x1 = new BN(1); - var x2 = new BN(0); - - var delta = b.clone(); - - while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { - for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); - if (i > 0) { - a.iushrn(i); - while (i-- > 0) { - if (x1.isOdd()) { - x1.iadd(delta); - } - - x1.iushrn(1); - } - } - - for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); - if (j > 0) { - b.iushrn(j); - while (j-- > 0) { - if (x2.isOdd()) { - x2.iadd(delta); - } - - x2.iushrn(1); - } - } - - if (a.cmp(b) >= 0) { - a.isub(b); - x1.isub(x2); - } else { - b.isub(a); - x2.isub(x1); - } - } - - var res; - if (a.cmpn(1) === 0) { - res = x1; - } else { - res = x2; - } - - if (res.cmpn(0) < 0) { - res.iadd(p); - } - - return res; - }; - - BN.prototype.gcd = function gcd (num) { - if (this.isZero()) return num.abs(); - if (num.isZero()) return this.abs(); - - var a = this.clone(); - var b = num.clone(); - a.negative = 0; - b.negative = 0; - - // Remove common factor of two - for (var shift = 0; a.isEven() && b.isEven(); shift++) { - a.iushrn(1); - b.iushrn(1); - } - - do { - while (a.isEven()) { - a.iushrn(1); - } - while (b.isEven()) { - b.iushrn(1); - } - - var r = a.cmp(b); - if (r < 0) { - // Swap `a` and `b` to make `a` always bigger than `b` - var t = a; - a = b; - b = t; - } else if (r === 0 || b.cmpn(1) === 0) { - break; - } - - a.isub(b); - } while (true); - - return b.iushln(shift); - }; - - // Invert number in the field F(num) - BN.prototype.invm = function invm (num) { - return this.egcd(num).a.umod(num); - }; - - BN.prototype.isEven = function isEven () { - return (this.words[0] & 1) === 0; - }; - - BN.prototype.isOdd = function isOdd () { - return (this.words[0] & 1) === 1; - }; - - // And first word and num - BN.prototype.andln = function andln (num) { - return this.words[0] & num; - }; - - // Increment at the bit position in-line - BN.prototype.bincn = function bincn (bit) { - assert(typeof bit === 'number'); - var r = bit % 26; - var s = (bit - r) / 26; - var q = 1 << r; - - // Fast case: bit is much higher than all existing words - if (this.length <= s) { - this._expand(s + 1); - this.words[s] |= q; - return this; - } - - // Add bit and propagate, if needed - var carry = q; - for (var i = s; carry !== 0 && i < this.length; i++) { - var w = this.words[i] | 0; - w += carry; - carry = w >>> 26; - w &= 0x3ffffff; - this.words[i] = w; - } - if (carry !== 0) { - this.words[i] = carry; - this.length++; - } - return this; - }; - - BN.prototype.isZero = function isZero () { - return this.length === 1 && this.words[0] === 0; - }; - - BN.prototype.cmpn = function cmpn (num) { - var negative = num < 0; - - if (this.negative !== 0 && !negative) return -1; - if (this.negative === 0 && negative) return 1; - - this._strip(); - - var res; - if (this.length > 1) { - res = 1; - } else { - if (negative) { - num = -num; - } - - assert(num <= 0x3ffffff, 'Number is too big'); - - var w = this.words[0] | 0; - res = w === num ? 0 : w < num ? -1 : 1; - } - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Compare two numbers and return: - // 1 - if `this` > `num` - // 0 - if `this` == `num` - // -1 - if `this` < `num` - BN.prototype.cmp = function cmp (num) { - if (this.negative !== 0 && num.negative === 0) return -1; - if (this.negative === 0 && num.negative !== 0) return 1; - - var res = this.ucmp(num); - if (this.negative !== 0) return -res | 0; - return res; - }; - - // Unsigned comparison - BN.prototype.ucmp = function ucmp (num) { - // At this point both numbers have the same sign - if (this.length > num.length) return 1; - if (this.length < num.length) return -1; - - var res = 0; - for (var i = this.length - 1; i >= 0; i--) { - var a = this.words[i] | 0; - var b = num.words[i] | 0; - - if (a === b) continue; - if (a < b) { - res = -1; - } else if (a > b) { - res = 1; - } - break; - } - return res; - }; - - BN.prototype.gtn = function gtn (num) { - return this.cmpn(num) === 1; - }; - - BN.prototype.gt = function gt (num) { - return this.cmp(num) === 1; - }; - - BN.prototype.gten = function gten (num) { - return this.cmpn(num) >= 0; - }; - - BN.prototype.gte = function gte (num) { - return this.cmp(num) >= 0; - }; - - BN.prototype.ltn = function ltn (num) { - return this.cmpn(num) === -1; - }; - - BN.prototype.lt = function lt (num) { - return this.cmp(num) === -1; - }; - - BN.prototype.lten = function lten (num) { - return this.cmpn(num) <= 0; - }; - - BN.prototype.lte = function lte (num) { - return this.cmp(num) <= 0; - }; - - BN.prototype.eqn = function eqn (num) { - return this.cmpn(num) === 0; - }; - - BN.prototype.eq = function eq (num) { - return this.cmp(num) === 0; - }; - - // - // A reduce context, could be using montgomery or something better, depending - // on the `m` itself. - // - BN.red = function red (num) { - return new Red(num); - }; - - BN.prototype.toRed = function toRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - assert(this.negative === 0, 'red works only with positives'); - return ctx.convertTo(this)._forceRed(ctx); - }; - - BN.prototype.fromRed = function fromRed () { - assert(this.red, 'fromRed works only with numbers in reduction context'); - return this.red.convertFrom(this); - }; - - BN.prototype._forceRed = function _forceRed (ctx) { - this.red = ctx; - return this; - }; - - BN.prototype.forceRed = function forceRed (ctx) { - assert(!this.red, 'Already a number in reduction context'); - return this._forceRed(ctx); - }; - - BN.prototype.redAdd = function redAdd (num) { - assert(this.red, 'redAdd works only with red numbers'); - return this.red.add(this, num); - }; - - BN.prototype.redIAdd = function redIAdd (num) { - assert(this.red, 'redIAdd works only with red numbers'); - return this.red.iadd(this, num); - }; - - BN.prototype.redSub = function redSub (num) { - assert(this.red, 'redSub works only with red numbers'); - return this.red.sub(this, num); - }; - - BN.prototype.redISub = function redISub (num) { - assert(this.red, 'redISub works only with red numbers'); - return this.red.isub(this, num); - }; - - BN.prototype.redShl = function redShl (num) { - assert(this.red, 'redShl works only with red numbers'); - return this.red.shl(this, num); - }; - - BN.prototype.redMul = function redMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.mul(this, num); - }; - - BN.prototype.redIMul = function redIMul (num) { - assert(this.red, 'redMul works only with red numbers'); - this.red._verify2(this, num); - return this.red.imul(this, num); - }; - - BN.prototype.redSqr = function redSqr () { - assert(this.red, 'redSqr works only with red numbers'); - this.red._verify1(this); - return this.red.sqr(this); - }; - - BN.prototype.redISqr = function redISqr () { - assert(this.red, 'redISqr works only with red numbers'); - this.red._verify1(this); - return this.red.isqr(this); - }; - - // Square root over p - BN.prototype.redSqrt = function redSqrt () { - assert(this.red, 'redSqrt works only with red numbers'); - this.red._verify1(this); - return this.red.sqrt(this); - }; - - BN.prototype.redInvm = function redInvm () { - assert(this.red, 'redInvm works only with red numbers'); - this.red._verify1(this); - return this.red.invm(this); - }; - - // Return negative clone of `this` % `red modulo` - BN.prototype.redNeg = function redNeg () { - assert(this.red, 'redNeg works only with red numbers'); - this.red._verify1(this); - return this.red.neg(this); - }; - - BN.prototype.redPow = function redPow (num) { - assert(this.red && !num.red, 'redPow(normalNum)'); - this.red._verify1(this); - return this.red.pow(this, num); - }; - - // Prime numbers with efficient reduction - var primes = { - k256: null, - p224: null, - p192: null, - p25519: null - }; - - // Pseudo-Mersenne prime - function MPrime (name, p) { - // P = 2 ^ N - K - this.name = name; - this.p = new BN(p, 16); - this.n = this.p.bitLength(); - this.k = new BN(1).iushln(this.n).isub(this.p); - - this.tmp = this._tmp(); - } - - MPrime.prototype._tmp = function _tmp () { - var tmp = new BN(null); - tmp.words = new Array(Math.ceil(this.n / 13)); - return tmp; - }; - - MPrime.prototype.ireduce = function ireduce (num) { - // Assumes that `num` is less than `P^2` - // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) - var r = num; - var rlen; - - do { - this.split(r, this.tmp); - r = this.imulK(r); - r = r.iadd(this.tmp); - rlen = r.bitLength(); - } while (rlen > this.n); - - var cmp = rlen < this.n ? -1 : r.ucmp(this.p); - if (cmp === 0) { - r.words[0] = 0; - r.length = 1; - } else if (cmp > 0) { - r.isub(this.p); - } else { - if (r.strip !== undefined) { - // r is a BN v4 instance - r.strip(); - } else { - // r is a BN v5 instance - r._strip(); - } - } - - return r; - }; - - MPrime.prototype.split = function split (input, out) { - input.iushrn(this.n, 0, out); - }; - - MPrime.prototype.imulK = function imulK (num) { - return num.imul(this.k); - }; - - function K256 () { - MPrime.call( - this, - 'k256', - 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); - } - inherits(K256, MPrime); - - K256.prototype.split = function split (input, output) { - // 256 = 9 * 26 + 22 - var mask = 0x3fffff; - - var outLen = Math.min(input.length, 9); - for (var i = 0; i < outLen; i++) { - output.words[i] = input.words[i]; - } - output.length = outLen; - - if (input.length <= 9) { - input.words[0] = 0; - input.length = 1; - return; - } - - // Shift by 9 limbs - var prev = input.words[9]; - output.words[output.length++] = prev & mask; - - for (i = 10; i < input.length; i++) { - var next = input.words[i] | 0; - input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); - prev = next; - } - prev >>>= 22; - input.words[i - 10] = prev; - if (prev === 0 && input.length > 10) { - input.length -= 10; - } else { - input.length -= 9; - } - }; - - K256.prototype.imulK = function imulK (num) { - // K = 0x1000003d1 = [ 0x40, 0x3d1 ] - num.words[num.length] = 0; - num.words[num.length + 1] = 0; - num.length += 2; - - // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 - var lo = 0; - for (var i = 0; i < num.length; i++) { - var w = num.words[i] | 0; - lo += w * 0x3d1; - num.words[i] = lo & 0x3ffffff; - lo = w * 0x40 + ((lo / 0x4000000) | 0); - } - - // Fast length reduction - if (num.words[num.length - 1] === 0) { - num.length--; - if (num.words[num.length - 1] === 0) { - num.length--; - } - } - return num; - }; - - function P224 () { - MPrime.call( - this, - 'p224', - 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); - } - inherits(P224, MPrime); - - function P192 () { - MPrime.call( - this, - 'p192', - 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); - } - inherits(P192, MPrime); - - function P25519 () { - // 2 ^ 255 - 19 - MPrime.call( - this, - '25519', - '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); - } - inherits(P25519, MPrime); - - P25519.prototype.imulK = function imulK (num) { - // K = 0x13 - var carry = 0; - for (var i = 0; i < num.length; i++) { - var hi = (num.words[i] | 0) * 0x13 + carry; - var lo = hi & 0x3ffffff; - hi >>>= 26; - - num.words[i] = lo; - carry = hi; - } - if (carry !== 0) { - num.words[num.length++] = carry; - } - return num; - }; - - // Exported mostly for testing purposes, use plain name instead - BN._prime = function prime (name) { - // Cached version of prime - if (primes[name]) return primes[name]; - - var prime; - if (name === 'k256') { - prime = new K256(); - } else if (name === 'p224') { - prime = new P224(); - } else if (name === 'p192') { - prime = new P192(); - } else if (name === 'p25519') { - prime = new P25519(); - } else { - throw new Error('Unknown prime ' + name); - } - primes[name] = prime; - - return prime; - }; - - // - // Base reduction engine - // - function Red (m) { - if (typeof m === 'string') { - var prime = BN._prime(m); - this.m = prime.p; - this.prime = prime; - } else { - assert(m.gtn(1), 'modulus must be greater than 1'); - this.m = m; - this.prime = null; - } - } - - Red.prototype._verify1 = function _verify1 (a) { - assert(a.negative === 0, 'red works only with positives'); - assert(a.red, 'red works only with red numbers'); - }; - - Red.prototype._verify2 = function _verify2 (a, b) { - assert((a.negative | b.negative) === 0, 'red works only with positives'); - assert(a.red && a.red === b.red, - 'red works only with red numbers'); - }; - - Red.prototype.imod = function imod (a) { - if (this.prime) return this.prime.ireduce(a)._forceRed(this); - - move(a, a.umod(this.m)._forceRed(this)); - return a; - }; - - Red.prototype.neg = function neg (a) { - if (a.isZero()) { - return a.clone(); - } - - return this.m.sub(a)._forceRed(this); - }; - - Red.prototype.add = function add (a, b) { - this._verify2(a, b); - - var res = a.add(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.iadd = function iadd (a, b) { - this._verify2(a, b); - - var res = a.iadd(b); - if (res.cmp(this.m) >= 0) { - res.isub(this.m); - } - return res; - }; - - Red.prototype.sub = function sub (a, b) { - this._verify2(a, b); - - var res = a.sub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res._forceRed(this); - }; - - Red.prototype.isub = function isub (a, b) { - this._verify2(a, b); - - var res = a.isub(b); - if (res.cmpn(0) < 0) { - res.iadd(this.m); - } - return res; - }; - - Red.prototype.shl = function shl (a, num) { - this._verify1(a); - return this.imod(a.ushln(num)); - }; - - Red.prototype.imul = function imul (a, b) { - this._verify2(a, b); - return this.imod(a.imul(b)); - }; - - Red.prototype.mul = function mul (a, b) { - this._verify2(a, b); - return this.imod(a.mul(b)); - }; - - Red.prototype.isqr = function isqr (a) { - return this.imul(a, a.clone()); - }; - - Red.prototype.sqr = function sqr (a) { - return this.mul(a, a); - }; - - Red.prototype.sqrt = function sqrt (a) { - if (a.isZero()) return a.clone(); - - var mod3 = this.m.andln(3); - assert(mod3 % 2 === 1); - - // Fast case - if (mod3 === 3) { - var pow = this.m.add(new BN(1)).iushrn(2); - return this.pow(a, pow); - } - - // Tonelli-Shanks algorithm (Totally unoptimized and slow) - // - // Find Q and S, that Q * 2 ^ S = (P - 1) - var q = this.m.subn(1); - var s = 0; - while (!q.isZero() && q.andln(1) === 0) { - s++; - q.iushrn(1); - } - assert(!q.isZero()); - - var one = new BN(1).toRed(this); - var nOne = one.redNeg(); - - // Find quadratic non-residue - // NOTE: Max is such because of generalized Riemann hypothesis. - var lpow = this.m.subn(1).iushrn(1); - var z = this.m.bitLength(); - z = new BN(2 * z * z).toRed(this); - - while (this.pow(z, lpow).cmp(nOne) !== 0) { - z.redIAdd(nOne); - } - - var c = this.pow(z, q); - var r = this.pow(a, q.addn(1).iushrn(1)); - var t = this.pow(a, q); - var m = s; - while (t.cmp(one) !== 0) { - var tmp = t; - for (var i = 0; tmp.cmp(one) !== 0; i++) { - tmp = tmp.redSqr(); - } - assert(i < m); - var b = this.pow(c, new BN(1).iushln(m - i - 1)); - - r = r.redMul(b); - c = b.redSqr(); - t = t.redMul(c); - m = i; - } - - return r; - }; - - Red.prototype.invm = function invm (a) { - var inv = a._invmp(this.m); - if (inv.negative !== 0) { - inv.negative = 0; - return this.imod(inv).redNeg(); - } else { - return this.imod(inv); - } - }; - - Red.prototype.pow = function pow (a, num) { - if (num.isZero()) return new BN(1).toRed(this); - if (num.cmpn(1) === 0) return a.clone(); - - var windowSize = 4; - var wnd = new Array(1 << windowSize); - wnd[0] = new BN(1).toRed(this); - wnd[1] = a; - for (var i = 2; i < wnd.length; i++) { - wnd[i] = this.mul(wnd[i - 1], a); - } - - var res = wnd[0]; - var current = 0; - var currentLen = 0; - var start = num.bitLength() % 26; - if (start === 0) { - start = 26; - } - - for (i = num.length - 1; i >= 0; i--) { - var word = num.words[i]; - for (var j = start - 1; j >= 0; j--) { - var bit = (word >> j) & 1; - if (res !== wnd[0]) { - res = this.sqr(res); - } - - if (bit === 0 && current === 0) { - currentLen = 0; - continue; - } - - current <<= 1; - current |= bit; - currentLen++; - if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; - - res = this.mul(res, wnd[current]); - currentLen = 0; - current = 0; - } - start = 26; - } - - return res; - }; - - Red.prototype.convertTo = function convertTo (num) { - var r = num.umod(this.m); - - return r === num ? r.clone() : r; - }; - - Red.prototype.convertFrom = function convertFrom (num) { - var res = num.clone(); - res.red = null; - return res; - }; - - // - // Montgomery method engine - // - - BN.mont = function mont (num) { - return new Mont(num); - }; - - function Mont (m) { - Red.call(this, m); - - this.shift = this.m.bitLength(); - if (this.shift % 26 !== 0) { - this.shift += 26 - (this.shift % 26); - } - - this.r = new BN(1).iushln(this.shift); - this.r2 = this.imod(this.r.sqr()); - this.rinv = this.r._invmp(this.m); - - this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); - this.minv = this.minv.umod(this.r); - this.minv = this.r.sub(this.minv); - } - inherits(Mont, Red); - - Mont.prototype.convertTo = function convertTo (num) { - return this.imod(num.ushln(this.shift)); - }; - - Mont.prototype.convertFrom = function convertFrom (num) { - var r = this.imod(num.mul(this.rinv)); - r.red = null; - return r; - }; - - Mont.prototype.imul = function imul (a, b) { - if (a.isZero() || b.isZero()) { - a.words[0] = 0; - a.length = 1; - return a; - } - - var t = a.imul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.mul = function mul (a, b) { - if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); - - var t = a.mul(b); - var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); - var u = t.isub(c).iushrn(this.shift); - var res = u; - if (u.cmp(this.m) >= 0) { - res = u.isub(this.m); - } else if (u.cmpn(0) < 0) { - res = u.iadd(this.m); - } - - return res._forceRed(this); - }; - - Mont.prototype.invm = function invm (a) { - // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R - var res = this.imod(a._invmp(this.m).mul(this.r2)); - return res._forceRed(this); - }; -})(typeof module === 'undefined' || module, this); - -},{"buffer":91}],90:[function(require,module,exports){ -var r; - -module.exports = function rand(len) { - if (!r) - r = new Rand(null); - - return r.generate(len); -}; - -function Rand(rand) { - this.rand = rand; -} -module.exports.Rand = Rand; - -Rand.prototype.generate = function generate(len) { - return this._rand(len); -}; - -// Emulate crypto API using randy -Rand.prototype._rand = function _rand(n) { - if (this.rand.getBytes) - return this.rand.getBytes(n); - - var res = new Uint8Array(n); - for (var i = 0; i < res.length; i++) - res[i] = this.rand.getByte(); - return res; -}; - -if (typeof self === 'object') { - if (self.crypto && self.crypto.getRandomValues) { - // Modern browsers - Rand.prototype._rand = function _rand(n) { - var arr = new Uint8Array(n); - self.crypto.getRandomValues(arr); - return arr; - }; - } else if (self.msCrypto && self.msCrypto.getRandomValues) { - // IE - Rand.prototype._rand = function _rand(n) { - var arr = new Uint8Array(n); - self.msCrypto.getRandomValues(arr); - return arr; - }; - - // Safari's WebWorkers do not have `crypto` - } else if (typeof window === 'object') { - // Old junk - Rand.prototype._rand = function() { - throw new Error('Not implemented yet'); - }; - } -} else { - // Node.js or Web worker with no crypto support - try { - var crypto = require('crypto'); - if (typeof crypto.randomBytes !== 'function') - throw new Error('Not supported'); - - Rand.prototype._rand = function _rand(n) { - return crypto.randomBytes(n); - }; - } catch (e) { - } -} - -},{"crypto":91}],91:[function(require,module,exports){ - -},{}],92:[function(require,module,exports){ -// based on the aes implimentation in triple sec -// https://github.com/keybase/triplesec -// which is in turn based on the one from crypto-js -// https://code.google.com/p/crypto-js/ - -var Buffer = require('safe-buffer').Buffer - -function asUInt32Array (buf) { - if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf) - - var len = (buf.length / 4) | 0 - var out = new Array(len) - - for (var i = 0; i < len; i++) { - out[i] = buf.readUInt32BE(i * 4) - } - - return out -} - -function scrubVec (v) { - for (var i = 0; i < v.length; v++) { - v[i] = 0 - } -} - -function cryptBlock (M, keySchedule, SUB_MIX, SBOX, nRounds) { - var SUB_MIX0 = SUB_MIX[0] - var SUB_MIX1 = SUB_MIX[1] - var SUB_MIX2 = SUB_MIX[2] - var SUB_MIX3 = SUB_MIX[3] - - var s0 = M[0] ^ keySchedule[0] - var s1 = M[1] ^ keySchedule[1] - var s2 = M[2] ^ keySchedule[2] - var s3 = M[3] ^ keySchedule[3] - var t0, t1, t2, t3 - var ksRow = 4 - - for (var round = 1; round < nRounds; round++) { - t0 = SUB_MIX0[s0 >>> 24] ^ SUB_MIX1[(s1 >>> 16) & 0xff] ^ SUB_MIX2[(s2 >>> 8) & 0xff] ^ SUB_MIX3[s3 & 0xff] ^ keySchedule[ksRow++] - t1 = SUB_MIX0[s1 >>> 24] ^ SUB_MIX1[(s2 >>> 16) & 0xff] ^ SUB_MIX2[(s3 >>> 8) & 0xff] ^ SUB_MIX3[s0 & 0xff] ^ keySchedule[ksRow++] - t2 = SUB_MIX0[s2 >>> 24] ^ SUB_MIX1[(s3 >>> 16) & 0xff] ^ SUB_MIX2[(s0 >>> 8) & 0xff] ^ SUB_MIX3[s1 & 0xff] ^ keySchedule[ksRow++] - t3 = SUB_MIX0[s3 >>> 24] ^ SUB_MIX1[(s0 >>> 16) & 0xff] ^ SUB_MIX2[(s1 >>> 8) & 0xff] ^ SUB_MIX3[s2 & 0xff] ^ keySchedule[ksRow++] - s0 = t0 - s1 = t1 - s2 = t2 - s3 = t3 - } - - t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++] - t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++] - t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++] - t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++] - t0 = t0 >>> 0 - t1 = t1 >>> 0 - t2 = t2 >>> 0 - t3 = t3 >>> 0 - - return [t0, t1, t2, t3] -} - -// AES constants -var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36] -var G = (function () { - // Compute double table - var d = new Array(256) - for (var j = 0; j < 256; j++) { - if (j < 128) { - d[j] = j << 1 - } else { - d[j] = (j << 1) ^ 0x11b - } - } - - var SBOX = [] - var INV_SBOX = [] - var SUB_MIX = [[], [], [], []] - var INV_SUB_MIX = [[], [], [], []] - - // Walk GF(2^8) - var x = 0 - var xi = 0 - for (var i = 0; i < 256; ++i) { - // Compute sbox - var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4) - sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63 - SBOX[x] = sx - INV_SBOX[sx] = x - - // Compute multiplication - var x2 = d[x] - var x4 = d[x2] - var x8 = d[x4] - - // Compute sub bytes, mix columns tables - var t = (d[sx] * 0x101) ^ (sx * 0x1010100) - SUB_MIX[0][x] = (t << 24) | (t >>> 8) - SUB_MIX[1][x] = (t << 16) | (t >>> 16) - SUB_MIX[2][x] = (t << 8) | (t >>> 24) - SUB_MIX[3][x] = t - - // Compute inv sub bytes, inv mix columns tables - t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100) - INV_SUB_MIX[0][sx] = (t << 24) | (t >>> 8) - INV_SUB_MIX[1][sx] = (t << 16) | (t >>> 16) - INV_SUB_MIX[2][sx] = (t << 8) | (t >>> 24) - INV_SUB_MIX[3][sx] = t - - if (x === 0) { - x = xi = 1 - } else { - x = x2 ^ d[d[d[x8 ^ x2]]] - xi ^= d[d[xi]] - } - } - - return { - SBOX: SBOX, - INV_SBOX: INV_SBOX, - SUB_MIX: SUB_MIX, - INV_SUB_MIX: INV_SUB_MIX - } -})() - -function AES (key) { - this._key = asUInt32Array(key) - this._reset() -} - -AES.blockSize = 4 * 4 -AES.keySize = 256 / 8 -AES.prototype.blockSize = AES.blockSize -AES.prototype.keySize = AES.keySize -AES.prototype._reset = function () { - var keyWords = this._key - var keySize = keyWords.length - var nRounds = keySize + 6 - var ksRows = (nRounds + 1) * 4 - - var keySchedule = [] - for (var k = 0; k < keySize; k++) { - keySchedule[k] = keyWords[k] - } - - for (k = keySize; k < ksRows; k++) { - var t = keySchedule[k - 1] - - if (k % keySize === 0) { - t = (t << 8) | (t >>> 24) - t = - (G.SBOX[t >>> 24] << 24) | - (G.SBOX[(t >>> 16) & 0xff] << 16) | - (G.SBOX[(t >>> 8) & 0xff] << 8) | - (G.SBOX[t & 0xff]) - - t ^= RCON[(k / keySize) | 0] << 24 - } else if (keySize > 6 && k % keySize === 4) { - t = - (G.SBOX[t >>> 24] << 24) | - (G.SBOX[(t >>> 16) & 0xff] << 16) | - (G.SBOX[(t >>> 8) & 0xff] << 8) | - (G.SBOX[t & 0xff]) - } - - keySchedule[k] = keySchedule[k - keySize] ^ t - } - - var invKeySchedule = [] - for (var ik = 0; ik < ksRows; ik++) { - var ksR = ksRows - ik - var tt = keySchedule[ksR - (ik % 4 ? 0 : 4)] - - if (ik < 4 || ksR <= 4) { - invKeySchedule[ik] = tt - } else { - invKeySchedule[ik] = - G.INV_SUB_MIX[0][G.SBOX[tt >>> 24]] ^ - G.INV_SUB_MIX[1][G.SBOX[(tt >>> 16) & 0xff]] ^ - G.INV_SUB_MIX[2][G.SBOX[(tt >>> 8) & 0xff]] ^ - G.INV_SUB_MIX[3][G.SBOX[tt & 0xff]] - } - } - - this._nRounds = nRounds - this._keySchedule = keySchedule - this._invKeySchedule = invKeySchedule -} - -AES.prototype.encryptBlockRaw = function (M) { - M = asUInt32Array(M) - return cryptBlock(M, this._keySchedule, G.SUB_MIX, G.SBOX, this._nRounds) -} - -AES.prototype.encryptBlock = function (M) { - var out = this.encryptBlockRaw(M) - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0], 0) - buf.writeUInt32BE(out[1], 4) - buf.writeUInt32BE(out[2], 8) - buf.writeUInt32BE(out[3], 12) - return buf -} - -AES.prototype.decryptBlock = function (M) { - M = asUInt32Array(M) - - // swap - var m1 = M[1] - M[1] = M[3] - M[3] = m1 - - var out = cryptBlock(M, this._invKeySchedule, G.INV_SUB_MIX, G.INV_SBOX, this._nRounds) - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0], 0) - buf.writeUInt32BE(out[3], 4) - buf.writeUInt32BE(out[2], 8) - buf.writeUInt32BE(out[1], 12) - return buf -} - -AES.prototype.scrub = function () { - scrubVec(this._keySchedule) - scrubVec(this._invKeySchedule) - scrubVec(this._key) -} - -module.exports.AES = AES - -},{"safe-buffer":390}],93:[function(require,module,exports){ -var aes = require('./aes') -var Buffer = require('safe-buffer').Buffer -var Transform = require('cipher-base') -var inherits = require('inherits') -var GHASH = require('./ghash') -var xor = require('buffer-xor') -var incr32 = require('./incr32') - -function xorTest (a, b) { - var out = 0 - if (a.length !== b.length) out++ - - var len = Math.min(a.length, b.length) - for (var i = 0; i < len; ++i) { - out += (a[i] ^ b[i]) - } - - return out -} - -function calcIv (self, iv, ck) { - if (iv.length === 12) { - self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])]) - return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])]) - } - var ghash = new GHASH(ck) - var len = iv.length - var toPad = len % 16 - ghash.update(iv) - if (toPad) { - toPad = 16 - toPad - ghash.update(Buffer.alloc(toPad, 0)) - } - ghash.update(Buffer.alloc(8, 0)) - var ivBits = len * 8 - var tail = Buffer.alloc(8) - tail.writeUIntBE(ivBits, 0, 8) - ghash.update(tail) - self._finID = ghash.state - var out = Buffer.from(self._finID) - incr32(out) - return out -} -function StreamCipher (mode, key, iv, decrypt) { - Transform.call(this) - - var h = Buffer.alloc(4, 0) - - this._cipher = new aes.AES(key) - var ck = this._cipher.encryptBlock(h) - this._ghash = new GHASH(ck) - iv = calcIv(this, iv, ck) - - this._prev = Buffer.from(iv) - this._cache = Buffer.allocUnsafe(0) - this._secCache = Buffer.allocUnsafe(0) - this._decrypt = decrypt - this._alen = 0 - this._len = 0 - this._mode = mode - - this._authTag = null - this._called = false -} - -inherits(StreamCipher, Transform) - -StreamCipher.prototype._update = function (chunk) { - if (!this._called && this._alen) { - var rump = 16 - (this._alen % 16) - if (rump < 16) { - rump = Buffer.alloc(rump, 0) - this._ghash.update(rump) - } - } - - this._called = true - var out = this._mode.encrypt(this, chunk) - if (this._decrypt) { - this._ghash.update(chunk) - } else { - this._ghash.update(out) - } - this._len += chunk.length - return out -} - -StreamCipher.prototype._final = function () { - if (this._decrypt && !this._authTag) throw new Error('Unsupported state or unable to authenticate data') - - var tag = xor(this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID)) - if (this._decrypt && xorTest(tag, this._authTag)) throw new Error('Unsupported state or unable to authenticate data') - - this._authTag = tag - this._cipher.scrub() -} - -StreamCipher.prototype.getAuthTag = function getAuthTag () { - if (this._decrypt || !Buffer.isBuffer(this._authTag)) throw new Error('Attempting to get auth tag in unsupported state') - - return this._authTag -} - -StreamCipher.prototype.setAuthTag = function setAuthTag (tag) { - if (!this._decrypt) throw new Error('Attempting to set auth tag in unsupported state') - - this._authTag = tag -} - -StreamCipher.prototype.setAAD = function setAAD (buf) { - if (this._called) throw new Error('Attempting to set AAD in unsupported state') - - this._ghash.update(buf) - this._alen += buf.length -} - -module.exports = StreamCipher - -},{"./aes":92,"./ghash":97,"./incr32":98,"buffer-xor":154,"cipher-base":159,"inherits":243,"safe-buffer":390}],94:[function(require,module,exports){ -var ciphers = require('./encrypter') -var deciphers = require('./decrypter') -var modes = require('./modes/list.json') - -function getCiphers () { - return Object.keys(modes) -} - -exports.createCipher = exports.Cipher = ciphers.createCipher -exports.createCipheriv = exports.Cipheriv = ciphers.createCipheriv -exports.createDecipher = exports.Decipher = deciphers.createDecipher -exports.createDecipheriv = exports.Decipheriv = deciphers.createDecipheriv -exports.listCiphers = exports.getCiphers = getCiphers - -},{"./decrypter":95,"./encrypter":96,"./modes/list.json":106}],95:[function(require,module,exports){ -var AuthCipher = require('./authCipher') -var Buffer = require('safe-buffer').Buffer -var MODES = require('./modes') -var StreamCipher = require('./streamCipher') -var Transform = require('cipher-base') -var aes = require('./aes') -var ebtk = require('evp_bytestokey') -var inherits = require('inherits') - -function Decipher (mode, key, iv) { - Transform.call(this) - - this._cache = new Splitter() - this._last = void 0 - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._mode = mode - this._autopadding = true -} - -inherits(Decipher, Transform) - -Decipher.prototype._update = function (data) { - this._cache.add(data) - var chunk - var thing - var out = [] - while ((chunk = this._cache.get(this._autopadding))) { - thing = this._mode.decrypt(this, chunk) - out.push(thing) - } - return Buffer.concat(out) -} - -Decipher.prototype._final = function () { - var chunk = this._cache.flush() - if (this._autopadding) { - return unpad(this._mode.decrypt(this, chunk)) - } else if (chunk) { - throw new Error('data not multiple of block length') - } -} - -Decipher.prototype.setAutoPadding = function (setTo) { - this._autopadding = !!setTo - return this -} - -function Splitter () { - this.cache = Buffer.allocUnsafe(0) -} - -Splitter.prototype.add = function (data) { - this.cache = Buffer.concat([this.cache, data]) -} - -Splitter.prototype.get = function (autoPadding) { - var out - if (autoPadding) { - if (this.cache.length > 16) { - out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - } else { - if (this.cache.length >= 16) { - out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - } - - return null -} - -Splitter.prototype.flush = function () { - if (this.cache.length) return this.cache -} - -function unpad (last) { - var padded = last[15] - if (padded < 1 || padded > 16) { - throw new Error('unable to decrypt data') - } - var i = -1 - while (++i < padded) { - if (last[(i + (16 - padded))] !== padded) { - throw new Error('unable to decrypt data') - } - } - if (padded === 16) return - - return last.slice(0, 16 - padded) -} - -function createDecipheriv (suite, password, iv) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - if (typeof iv === 'string') iv = Buffer.from(iv) - if (config.mode !== 'GCM' && iv.length !== config.iv) throw new TypeError('invalid iv length ' + iv.length) - - if (typeof password === 'string') password = Buffer.from(password) - if (password.length !== config.key / 8) throw new TypeError('invalid key length ' + password.length) - - if (config.type === 'stream') { - return new StreamCipher(config.module, password, iv, true) - } else if (config.type === 'auth') { - return new AuthCipher(config.module, password, iv, true) - } - - return new Decipher(config.module, password, iv) -} - -function createDecipher (suite, password) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - var keys = ebtk(password, false, config.key, config.iv) - return createDecipheriv(suite, keys.key, keys.iv) -} - -exports.createDecipher = createDecipher -exports.createDecipheriv = createDecipheriv - -},{"./aes":92,"./authCipher":93,"./modes":105,"./streamCipher":108,"cipher-base":159,"evp_bytestokey":202,"inherits":243,"safe-buffer":390}],96:[function(require,module,exports){ -var MODES = require('./modes') -var AuthCipher = require('./authCipher') -var Buffer = require('safe-buffer').Buffer -var StreamCipher = require('./streamCipher') -var Transform = require('cipher-base') -var aes = require('./aes') -var ebtk = require('evp_bytestokey') -var inherits = require('inherits') - -function Cipher (mode, key, iv) { - Transform.call(this) - - this._cache = new Splitter() - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._mode = mode - this._autopadding = true -} - -inherits(Cipher, Transform) - -Cipher.prototype._update = function (data) { - this._cache.add(data) - var chunk - var thing - var out = [] - - while ((chunk = this._cache.get())) { - thing = this._mode.encrypt(this, chunk) - out.push(thing) - } - - return Buffer.concat(out) -} - -var PADDING = Buffer.alloc(16, 0x10) - -Cipher.prototype._final = function () { - var chunk = this._cache.flush() - if (this._autopadding) { - chunk = this._mode.encrypt(this, chunk) - this._cipher.scrub() - return chunk - } - - if (!chunk.equals(PADDING)) { - this._cipher.scrub() - throw new Error('data not multiple of block length') - } -} - -Cipher.prototype.setAutoPadding = function (setTo) { - this._autopadding = !!setTo - return this -} - -function Splitter () { - this.cache = Buffer.allocUnsafe(0) -} - -Splitter.prototype.add = function (data) { - this.cache = Buffer.concat([this.cache, data]) -} - -Splitter.prototype.get = function () { - if (this.cache.length > 15) { - var out = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - return out - } - return null -} - -Splitter.prototype.flush = function () { - var len = 16 - this.cache.length - var padBuff = Buffer.allocUnsafe(len) - - var i = -1 - while (++i < len) { - padBuff.writeUInt8(len, i) - } - - return Buffer.concat([this.cache, padBuff]) -} - -function createCipheriv (suite, password, iv) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - if (typeof password === 'string') password = Buffer.from(password) - if (password.length !== config.key / 8) throw new TypeError('invalid key length ' + password.length) - - if (typeof iv === 'string') iv = Buffer.from(iv) - if (config.mode !== 'GCM' && iv.length !== config.iv) throw new TypeError('invalid iv length ' + iv.length) - - if (config.type === 'stream') { - return new StreamCipher(config.module, password, iv) - } else if (config.type === 'auth') { - return new AuthCipher(config.module, password, iv) - } - - return new Cipher(config.module, password, iv) -} - -function createCipher (suite, password) { - var config = MODES[suite.toLowerCase()] - if (!config) throw new TypeError('invalid suite type') - - var keys = ebtk(password, false, config.key, config.iv) - return createCipheriv(suite, keys.key, keys.iv) -} - -exports.createCipheriv = createCipheriv -exports.createCipher = createCipher - -},{"./aes":92,"./authCipher":93,"./modes":105,"./streamCipher":108,"cipher-base":159,"evp_bytestokey":202,"inherits":243,"safe-buffer":390}],97:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var ZEROES = Buffer.alloc(16, 0) - -function toArray (buf) { - return [ - buf.readUInt32BE(0), - buf.readUInt32BE(4), - buf.readUInt32BE(8), - buf.readUInt32BE(12) - ] -} - -function fromArray (out) { - var buf = Buffer.allocUnsafe(16) - buf.writeUInt32BE(out[0] >>> 0, 0) - buf.writeUInt32BE(out[1] >>> 0, 4) - buf.writeUInt32BE(out[2] >>> 0, 8) - buf.writeUInt32BE(out[3] >>> 0, 12) - return buf -} - -function GHASH (key) { - this.h = key - this.state = Buffer.alloc(16, 0) - this.cache = Buffer.allocUnsafe(0) -} - -// from http://bitwiseshiftleft.github.io/sjcl/doc/symbols/src/core_gcm.js.html -// by Juho Vähä-Herttua -GHASH.prototype.ghash = function (block) { - var i = -1 - while (++i < block.length) { - this.state[i] ^= block[i] - } - this._multiply() -} - -GHASH.prototype._multiply = function () { - var Vi = toArray(this.h) - var Zi = [0, 0, 0, 0] - var j, xi, lsbVi - var i = -1 - while (++i < 128) { - xi = (this.state[~~(i / 8)] & (1 << (7 - (i % 8)))) !== 0 - if (xi) { - // Z_i+1 = Z_i ^ V_i - Zi[0] ^= Vi[0] - Zi[1] ^= Vi[1] - Zi[2] ^= Vi[2] - Zi[3] ^= Vi[3] - } - - // Store the value of LSB(V_i) - lsbVi = (Vi[3] & 1) !== 0 - - // V_i+1 = V_i >> 1 - for (j = 3; j > 0; j--) { - Vi[j] = (Vi[j] >>> 1) | ((Vi[j - 1] & 1) << 31) - } - Vi[0] = Vi[0] >>> 1 - - // If LSB(V_i) is 1, V_i+1 = (V_i >> 1) ^ R - if (lsbVi) { - Vi[0] = Vi[0] ^ (0xe1 << 24) - } - } - this.state = fromArray(Zi) -} - -GHASH.prototype.update = function (buf) { - this.cache = Buffer.concat([this.cache, buf]) - var chunk - while (this.cache.length >= 16) { - chunk = this.cache.slice(0, 16) - this.cache = this.cache.slice(16) - this.ghash(chunk) - } -} - -GHASH.prototype.final = function (abl, bl) { - if (this.cache.length) { - this.ghash(Buffer.concat([this.cache, ZEROES], 16)) - } - - this.ghash(fromArray([0, abl, 0, bl])) - return this.state -} - -module.exports = GHASH - -},{"safe-buffer":390}],98:[function(require,module,exports){ -function incr32 (iv) { - var len = iv.length - var item - while (len--) { - item = iv.readUInt8(len) - if (item === 255) { - iv.writeUInt8(0, len) - } else { - item++ - iv.writeUInt8(item, len) - break - } - } -} -module.exports = incr32 - -},{}],99:[function(require,module,exports){ -var xor = require('buffer-xor') - -exports.encrypt = function (self, block) { - var data = xor(block, self._prev) - - self._prev = self._cipher.encryptBlock(data) - return self._prev -} - -exports.decrypt = function (self, block) { - var pad = self._prev - - self._prev = block - var out = self._cipher.decryptBlock(block) - - return xor(out, pad) -} - -},{"buffer-xor":154}],100:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var xor = require('buffer-xor') - -function encryptStart (self, data, decrypt) { - var len = data.length - var out = xor(data, self._cache) - self._cache = self._cache.slice(len) - self._prev = Buffer.concat([self._prev, decrypt ? data : out]) - return out -} - -exports.encrypt = function (self, data, decrypt) { - var out = Buffer.allocUnsafe(0) - var len - - while (data.length) { - if (self._cache.length === 0) { - self._cache = self._cipher.encryptBlock(self._prev) - self._prev = Buffer.allocUnsafe(0) - } - - if (self._cache.length <= data.length) { - len = self._cache.length - out = Buffer.concat([out, encryptStart(self, data.slice(0, len), decrypt)]) - data = data.slice(len) - } else { - out = Buffer.concat([out, encryptStart(self, data, decrypt)]) - break - } - } - - return out -} - -},{"buffer-xor":154,"safe-buffer":390}],101:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -function encryptByte (self, byteParam, decrypt) { - var pad - var i = -1 - var len = 8 - var out = 0 - var bit, value - while (++i < len) { - pad = self._cipher.encryptBlock(self._prev) - bit = (byteParam & (1 << (7 - i))) ? 0x80 : 0 - value = pad[0] ^ bit - out += ((value & 0x80) >> (i % 8)) - self._prev = shiftIn(self._prev, decrypt ? bit : value) - } - return out -} - -function shiftIn (buffer, value) { - var len = buffer.length - var i = -1 - var out = Buffer.allocUnsafe(buffer.length) - buffer = Buffer.concat([buffer, Buffer.from([value])]) - - while (++i < len) { - out[i] = buffer[i] << 1 | buffer[i + 1] >> (7) - } - - return out -} - -exports.encrypt = function (self, chunk, decrypt) { - var len = chunk.length - var out = Buffer.allocUnsafe(len) - var i = -1 - - while (++i < len) { - out[i] = encryptByte(self, chunk[i], decrypt) - } - - return out -} - -},{"safe-buffer":390}],102:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer - -function encryptByte (self, byteParam, decrypt) { - var pad = self._cipher.encryptBlock(self._prev) - var out = pad[0] ^ byteParam - - self._prev = Buffer.concat([ - self._prev.slice(1), - Buffer.from([decrypt ? byteParam : out]) - ]) - - return out -} - -exports.encrypt = function (self, chunk, decrypt) { - var len = chunk.length - var out = Buffer.allocUnsafe(len) - var i = -1 - - while (++i < len) { - out[i] = encryptByte(self, chunk[i], decrypt) - } - - return out -} - -},{"safe-buffer":390}],103:[function(require,module,exports){ -var xor = require('buffer-xor') -var Buffer = require('safe-buffer').Buffer -var incr32 = require('../incr32') - -function getBlock (self) { - var out = self._cipher.encryptBlockRaw(self._prev) - incr32(self._prev) - return out -} - -var blockSize = 16 -exports.encrypt = function (self, chunk) { - var chunkNum = Math.ceil(chunk.length / blockSize) - var start = self._cache.length - self._cache = Buffer.concat([ - self._cache, - Buffer.allocUnsafe(chunkNum * blockSize) - ]) - for (var i = 0; i < chunkNum; i++) { - var out = getBlock(self) - var offset = start + i * blockSize - self._cache.writeUInt32BE(out[0], offset + 0) - self._cache.writeUInt32BE(out[1], offset + 4) - self._cache.writeUInt32BE(out[2], offset + 8) - self._cache.writeUInt32BE(out[3], offset + 12) - } - var pad = self._cache.slice(0, chunk.length) - self._cache = self._cache.slice(chunk.length) - return xor(chunk, pad) -} - -},{"../incr32":98,"buffer-xor":154,"safe-buffer":390}],104:[function(require,module,exports){ -exports.encrypt = function (self, block) { - return self._cipher.encryptBlock(block) -} - -exports.decrypt = function (self, block) { - return self._cipher.decryptBlock(block) -} - -},{}],105:[function(require,module,exports){ -var modeModules = { - ECB: require('./ecb'), - CBC: require('./cbc'), - CFB: require('./cfb'), - CFB8: require('./cfb8'), - CFB1: require('./cfb1'), - OFB: require('./ofb'), - CTR: require('./ctr'), - GCM: require('./ctr') -} - -var modes = require('./list.json') - -for (var key in modes) { - modes[key].module = modeModules[modes[key].mode] -} - -module.exports = modes - -},{"./cbc":99,"./cfb":100,"./cfb1":101,"./cfb8":102,"./ctr":103,"./ecb":104,"./list.json":106,"./ofb":107}],106:[function(require,module,exports){ -module.exports={ - "aes-128-ecb": { - "cipher": "AES", - "key": 128, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-192-ecb": { - "cipher": "AES", - "key": 192, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-256-ecb": { - "cipher": "AES", - "key": 256, - "iv": 0, - "mode": "ECB", - "type": "block" - }, - "aes-128-cbc": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-192-cbc": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-256-cbc": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes128": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes192": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes256": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CBC", - "type": "block" - }, - "aes-128-cfb": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-192-cfb": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-256-cfb": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB", - "type": "stream" - }, - "aes-128-cfb8": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-192-cfb8": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-256-cfb8": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB8", - "type": "stream" - }, - "aes-128-cfb1": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-192-cfb1": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-256-cfb1": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CFB1", - "type": "stream" - }, - "aes-128-ofb": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-192-ofb": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-256-ofb": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "OFB", - "type": "stream" - }, - "aes-128-ctr": { - "cipher": "AES", - "key": 128, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-192-ctr": { - "cipher": "AES", - "key": 192, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-256-ctr": { - "cipher": "AES", - "key": 256, - "iv": 16, - "mode": "CTR", - "type": "stream" - }, - "aes-128-gcm": { - "cipher": "AES", - "key": 128, - "iv": 12, - "mode": "GCM", - "type": "auth" - }, - "aes-192-gcm": { - "cipher": "AES", - "key": 192, - "iv": 12, - "mode": "GCM", - "type": "auth" - }, - "aes-256-gcm": { - "cipher": "AES", - "key": 256, - "iv": 12, - "mode": "GCM", - "type": "auth" - } -} - -},{}],107:[function(require,module,exports){ -(function (Buffer){(function (){ -var xor = require('buffer-xor') - -function getBlock (self) { - self._prev = self._cipher.encryptBlock(self._prev) - return self._prev -} - -exports.encrypt = function (self, chunk) { - while (self._cache.length < chunk.length) { - self._cache = Buffer.concat([self._cache, getBlock(self)]) - } - - var pad = self._cache.slice(0, chunk.length) - self._cache = self._cache.slice(chunk.length) - return xor(chunk, pad) -} - -}).call(this)}).call(this,require("buffer").Buffer) -},{"buffer":135,"buffer-xor":154}],108:[function(require,module,exports){ -var aes = require('./aes') -var Buffer = require('safe-buffer').Buffer -var Transform = require('cipher-base') -var inherits = require('inherits') - -function StreamCipher (mode, key, iv, decrypt) { - Transform.call(this) - - this._cipher = new aes.AES(key) - this._prev = Buffer.from(iv) - this._cache = Buffer.allocUnsafe(0) - this._secCache = Buffer.allocUnsafe(0) - this._decrypt = decrypt - this._mode = mode -} - -inherits(StreamCipher, Transform) - -StreamCipher.prototype._update = function (chunk) { - return this._mode.encrypt(this, chunk, this._decrypt) -} - -StreamCipher.prototype._final = function () { - this._cipher.scrub() -} - -module.exports = StreamCipher - -},{"./aes":92,"cipher-base":159,"inherits":243,"safe-buffer":390}],109:[function(require,module,exports){ -var DES = require('browserify-des') -var aes = require('browserify-aes/browser') -var aesModes = require('browserify-aes/modes') -var desModes = require('browserify-des/modes') -var ebtk = require('evp_bytestokey') - -function createCipher (suite, password) { - suite = suite.toLowerCase() - - var keyLen, ivLen - if (aesModes[suite]) { - keyLen = aesModes[suite].key - ivLen = aesModes[suite].iv - } else if (desModes[suite]) { - keyLen = desModes[suite].key * 8 - ivLen = desModes[suite].iv - } else { - throw new TypeError('invalid suite type') - } - - var keys = ebtk(password, false, keyLen, ivLen) - return createCipheriv(suite, keys.key, keys.iv) -} - -function createDecipher (suite, password) { - suite = suite.toLowerCase() - - var keyLen, ivLen - if (aesModes[suite]) { - keyLen = aesModes[suite].key - ivLen = aesModes[suite].iv - } else if (desModes[suite]) { - keyLen = desModes[suite].key * 8 - ivLen = desModes[suite].iv - } else { - throw new TypeError('invalid suite type') - } - - var keys = ebtk(password, false, keyLen, ivLen) - return createDecipheriv(suite, keys.key, keys.iv) -} - -function createCipheriv (suite, key, iv) { - suite = suite.toLowerCase() - if (aesModes[suite]) return aes.createCipheriv(suite, key, iv) - if (desModes[suite]) return new DES({ key: key, iv: iv, mode: suite }) - - throw new TypeError('invalid suite type') -} - -function createDecipheriv (suite, key, iv) { - suite = suite.toLowerCase() - if (aesModes[suite]) return aes.createDecipheriv(suite, key, iv) - if (desModes[suite]) return new DES({ key: key, iv: iv, mode: suite, decrypt: true }) - - throw new TypeError('invalid suite type') -} - -function getCiphers () { - return Object.keys(desModes).concat(aes.getCiphers()) -} - -exports.createCipher = exports.Cipher = createCipher -exports.createCipheriv = exports.Cipheriv = createCipheriv -exports.createDecipher = exports.Decipher = createDecipher -exports.createDecipheriv = exports.Decipheriv = createDecipheriv -exports.listCiphers = exports.getCiphers = getCiphers - -},{"browserify-aes/browser":94,"browserify-aes/modes":105,"browserify-des":110,"browserify-des/modes":111,"evp_bytestokey":202}],110:[function(require,module,exports){ -var CipherBase = require('cipher-base') -var des = require('des.js') -var inherits = require('inherits') -var Buffer = require('safe-buffer').Buffer - -var modes = { - 'des-ede3-cbc': des.CBC.instantiate(des.EDE), - 'des-ede3': des.EDE, - 'des-ede-cbc': des.CBC.instantiate(des.EDE), - 'des-ede': des.EDE, - 'des-cbc': des.CBC.instantiate(des.DES), - 'des-ecb': des.DES -} -modes.des = modes['des-cbc'] -modes.des3 = modes['des-ede3-cbc'] -module.exports = DES -inherits(DES, CipherBase) -function DES (opts) { - CipherBase.call(this) - var modeName = opts.mode.toLowerCase() - var mode = modes[modeName] - var type - if (opts.decrypt) { - type = 'decrypt' - } else { - type = 'encrypt' - } - var key = opts.key - if (!Buffer.isBuffer(key)) { - key = Buffer.from(key) - } - if (modeName === 'des-ede' || modeName === 'des-ede-cbc') { - key = Buffer.concat([key, key.slice(0, 8)]) - } - var iv = opts.iv - if (!Buffer.isBuffer(iv)) { - iv = Buffer.from(iv) - } - this._des = mode.create({ - key: key, - iv: iv, - type: type - }) -} -DES.prototype._update = function (data) { - return Buffer.from(this._des.update(data)) -} -DES.prototype._final = function () { - return Buffer.from(this._des.final()) -} - -},{"cipher-base":159,"des.js":171,"inherits":243,"safe-buffer":390}],111:[function(require,module,exports){ -exports['des-ecb'] = { - key: 8, - iv: 0 -} -exports['des-cbc'] = exports.des = { - key: 8, - iv: 8 -} -exports['des-ede3-cbc'] = exports.des3 = { - key: 24, - iv: 8 -} -exports['des-ede3'] = { - key: 24, - iv: 0 -} -exports['des-ede-cbc'] = { - key: 16, - iv: 8 -} -exports['des-ede'] = { - key: 16, - iv: 0 -} - -},{}],112:[function(require,module,exports){ -(function (Buffer){(function (){ -var BN = require('bn.js') -var randomBytes = require('randombytes') - -function blind (priv) { - var r = getr(priv) - var blinder = r.toRed(BN.mont(priv.modulus)).redPow(new BN(priv.publicExponent)).fromRed() - return { blinder: blinder, unblinder: r.invm(priv.modulus) } -} - -function getr (priv) { - var len = priv.modulus.byteLength() - var r - do { - r = new BN(randomBytes(len)) - } while (r.cmp(priv.modulus) >= 0 || !r.umod(priv.prime1) || !r.umod(priv.prime2)) - return r -} - -function crt (msg, priv) { - var blinds = blind(priv) - var len = priv.modulus.byteLength() - var blinded = new BN(msg).mul(blinds.blinder).umod(priv.modulus) - var c1 = blinded.toRed(BN.mont(priv.prime1)) - var c2 = blinded.toRed(BN.mont(priv.prime2)) - var qinv = priv.coefficient - var p = priv.prime1 - var q = priv.prime2 - var m1 = c1.redPow(priv.exponent1).fromRed() - var m2 = c2.redPow(priv.exponent2).fromRed() - var h = m1.isub(m2).imul(qinv).umod(p).imul(q) - return m2.iadd(h).imul(blinds.unblinder).umod(priv.modulus).toArrayLike(Buffer, 'be', len) -} -crt.getr = getr - -module.exports = crt - -}).call(this)}).call(this,require("buffer").Buffer) -},{"bn.js":89,"buffer":135,"randombytes":386}],113:[function(require,module,exports){ -module.exports = require('./browser/algorithms.json') - -},{"./browser/algorithms.json":114}],114:[function(require,module,exports){ -module.exports={ - "sha224WithRSAEncryption": { - "sign": "rsa", - "hash": "sha224", - "id": "302d300d06096086480165030402040500041c" - }, - "RSA-SHA224": { - "sign": "ecdsa/rsa", - "hash": "sha224", - "id": "302d300d06096086480165030402040500041c" - }, - "sha256WithRSAEncryption": { - "sign": "rsa", - "hash": "sha256", - "id": "3031300d060960864801650304020105000420" - }, - "RSA-SHA256": { - "sign": "ecdsa/rsa", - "hash": "sha256", - "id": "3031300d060960864801650304020105000420" - }, - "sha384WithRSAEncryption": { - "sign": "rsa", - "hash": "sha384", - "id": "3041300d060960864801650304020205000430" - }, - "RSA-SHA384": { - "sign": "ecdsa/rsa", - "hash": "sha384", - "id": "3041300d060960864801650304020205000430" - }, - "sha512WithRSAEncryption": { - "sign": "rsa", - "hash": "sha512", - "id": "3051300d060960864801650304020305000440" - }, - "RSA-SHA512": { - "sign": "ecdsa/rsa", - "hash": "sha512", - "id": "3051300d060960864801650304020305000440" - }, - "RSA-SHA1": { - "sign": "rsa", - "hash": "sha1", - "id": "3021300906052b0e03021a05000414" - }, - "ecdsa-with-SHA1": { - "sign": "ecdsa", - "hash": "sha1", - "id": "" - }, - "sha256": { - "sign": "ecdsa", - "hash": "sha256", - "id": "" - }, - "sha224": { - "sign": "ecdsa", - "hash": "sha224", - "id": "" - }, - "sha384": { - "sign": "ecdsa", - "hash": "sha384", - "id": "" - }, - "sha512": { - "sign": "ecdsa", - "hash": "sha512", - "id": "" - }, - "DSA-SHA": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA-SHA1": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA": { - "sign": "dsa", - "hash": "sha1", - "id": "" - }, - "DSA-WITH-SHA224": { - "sign": "dsa", - "hash": "sha224", - "id": "" - }, - "DSA-SHA224": { - "sign": "dsa", - "hash": "sha224", - "id": "" - }, - "DSA-WITH-SHA256": { - "sign": "dsa", - "hash": "sha256", - "id": "" - }, - "DSA-SHA256": { - "sign": "dsa", - "hash": "sha256", - "id": "" - }, - "DSA-WITH-SHA384": { - "sign": "dsa", - "hash": "sha384", - "id": "" - }, - "DSA-SHA384": { - "sign": "dsa", - "hash": "sha384", - "id": "" - }, - "DSA-WITH-SHA512": { - "sign": "dsa", - "hash": "sha512", - "id": "" - }, - "DSA-SHA512": { - "sign": "dsa", - "hash": "sha512", - "id": "" - }, - "DSA-RIPEMD160": { - "sign": "dsa", - "hash": "rmd160", - "id": "" - }, - "ripemd160WithRSA": { - "sign": "rsa", - "hash": "rmd160", - "id": "3021300906052b2403020105000414" - }, - "RSA-RIPEMD160": { - "sign": "rsa", - "hash": "rmd160", - "id": "3021300906052b2403020105000414" - }, - "md5WithRSAEncryption": { - "sign": "rsa", - "hash": "md5", - "id": "3020300c06082a864886f70d020505000410" - }, - "RSA-MD5": { - "sign": "rsa", - "hash": "md5", - "id": "3020300c06082a864886f70d020505000410" - } -} - -},{}],115:[function(require,module,exports){ -module.exports={ - "1.3.132.0.10": "secp256k1", - "1.3.132.0.33": "p224", - "1.2.840.10045.3.1.1": "p192", - "1.2.840.10045.3.1.7": "p256", - "1.3.132.0.34": "p384", - "1.3.132.0.35": "p521" -} - -},{}],116:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var createHash = require('create-hash') -var stream = require('readable-stream') -var inherits = require('inherits') -var sign = require('./sign') -var verify = require('./verify') - -var algorithms = require('./algorithms.json') -Object.keys(algorithms).forEach(function (key) { - algorithms[key].id = Buffer.from(algorithms[key].id, 'hex') - algorithms[key.toLowerCase()] = algorithms[key] -}) - -function Sign (algorithm) { - stream.Writable.call(this) - - var data = algorithms[algorithm] - if (!data) throw new Error('Unknown message digest') - - this._hashType = data.hash - this._hash = createHash(data.hash) - this._tag = data.id - this._signType = data.sign -} -inherits(Sign, stream.Writable) - -Sign.prototype._write = function _write (data, _, done) { - this._hash.update(data) - done() -} - -Sign.prototype.update = function update (data, enc) { - if (typeof data === 'string') data = Buffer.from(data, enc) - - this._hash.update(data) - return this -} - -Sign.prototype.sign = function signMethod (key, enc) { - this.end() - var hash = this._hash.digest() - var sig = sign(hash, key, this._hashType, this._signType, this._tag) - - return enc ? sig.toString(enc) : sig -} - -function Verify (algorithm) { - stream.Writable.call(this) - - var data = algorithms[algorithm] - if (!data) throw new Error('Unknown message digest') - - this._hash = createHash(data.hash) - this._tag = data.id - this._signType = data.sign -} -inherits(Verify, stream.Writable) - -Verify.prototype._write = function _write (data, _, done) { - this._hash.update(data) - done() -} - -Verify.prototype.update = function update (data, enc) { - if (typeof data === 'string') data = Buffer.from(data, enc) - - this._hash.update(data) - return this -} - -Verify.prototype.verify = function verifyMethod (key, sig, enc) { - if (typeof sig === 'string') sig = Buffer.from(sig, enc) - - this.end() - var hash = this._hash.digest() - return verify(sig, hash, key, this._signType, this._tag) -} - -function createSign (algorithm) { - return new Sign(algorithm) -} - -function createVerify (algorithm) { - return new Verify(algorithm) -} - -module.exports = { - Sign: createSign, - Verify: createVerify, - createSign: createSign, - createVerify: createVerify -} - -},{"./algorithms.json":114,"./sign":117,"./verify":118,"create-hash":162,"inherits":243,"readable-stream":133,"safe-buffer":134}],117:[function(require,module,exports){ -// much of this based on https://github.com/indutny/self-signed/blob/gh-pages/lib/rsa.js -var Buffer = require('safe-buffer').Buffer -var createHmac = require('create-hmac') -var crt = require('browserify-rsa') -var EC = require('elliptic').ec -var BN = require('bn.js') -var parseKeys = require('parse-asn1') -var curves = require('./curves.json') - -function sign (hash, key, hashType, signType, tag) { - var priv = parseKeys(key) - if (priv.curve) { - // rsa keys can be interpreted as ecdsa ones in openssl - if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa') throw new Error('wrong private key type') - return ecSign(hash, priv) - } else if (priv.type === 'dsa') { - if (signType !== 'dsa') throw new Error('wrong private key type') - return dsaSign(hash, priv, hashType) - } else { - if (signType !== 'rsa' && signType !== 'ecdsa/rsa') throw new Error('wrong private key type') - } - hash = Buffer.concat([tag, hash]) - var len = priv.modulus.byteLength() - var pad = [0, 1] - while (hash.length + pad.length + 1 < len) pad.push(0xff) - pad.push(0x00) - var i = -1 - while (++i < hash.length) pad.push(hash[i]) - - var out = crt(pad, priv) - return out -} - -function ecSign (hash, priv) { - var curveId = curves[priv.curve.join('.')] - if (!curveId) throw new Error('unknown curve ' + priv.curve.join('.')) - - var curve = new EC(curveId) - var key = curve.keyFromPrivate(priv.privateKey) - var out = key.sign(hash) - - return Buffer.from(out.toDER()) -} - -function dsaSign (hash, priv, algo) { - var x = priv.params.priv_key - var p = priv.params.p - var q = priv.params.q - var g = priv.params.g - var r = new BN(0) - var k - var H = bits2int(hash, q).mod(q) - var s = false - var kv = getKey(x, q, hash, algo) - while (s === false) { - k = makeKey(q, kv, algo) - r = makeR(g, k, p, q) - s = k.invm(q).imul(H.add(x.mul(r))).mod(q) - if (s.cmpn(0) === 0) { - s = false - r = new BN(0) - } - } - return toDER(r, s) -} - -function toDER (r, s) { - r = r.toArray() - s = s.toArray() - - // Pad values - if (r[0] & 0x80) r = [0].concat(r) - if (s[0] & 0x80) s = [0].concat(s) - - var total = r.length + s.length + 4 - var res = [0x30, total, 0x02, r.length] - res = res.concat(r, [0x02, s.length], s) - return Buffer.from(res) -} - -function getKey (x, q, hash, algo) { - x = Buffer.from(x.toArray()) - if (x.length < q.byteLength()) { - var zeros = Buffer.alloc(q.byteLength() - x.length) - x = Buffer.concat([zeros, x]) - } - var hlen = hash.length - var hbits = bits2octets(hash, q) - var v = Buffer.alloc(hlen) - v.fill(1) - var k = Buffer.alloc(hlen) - k = createHmac(algo, k).update(v).update(Buffer.from([0])).update(x).update(hbits).digest() - v = createHmac(algo, k).update(v).digest() - k = createHmac(algo, k).update(v).update(Buffer.from([1])).update(x).update(hbits).digest() - v = createHmac(algo, k).update(v).digest() - return { k: k, v: v } -} - -function bits2int (obits, q) { - var bits = new BN(obits) - var shift = (obits.length << 3) - q.bitLength() - if (shift > 0) bits.ishrn(shift) - return bits -} - -function bits2octets (bits, q) { - bits = bits2int(bits, q) - bits = bits.mod(q) - var out = Buffer.from(bits.toArray()) - if (out.length < q.byteLength()) { - var zeros = Buffer.alloc(q.byteLength() - out.length) - out = Buffer.concat([zeros, out]) - } - return out -} - -function makeKey (q, kv, algo) { - var t - var k - - do { - t = Buffer.alloc(0) - - while (t.length * 8 < q.bitLength()) { - kv.v = createHmac(algo, kv.k).update(kv.v).digest() - t = Buffer.concat([t, kv.v]) - } - - k = bits2int(t, q) - kv.k = createHmac(algo, kv.k).update(kv.v).update(Buffer.from([0])).digest() - kv.v = createHmac(algo, kv.k).update(kv.v).digest() - } while (k.cmp(q) !== -1) - - return k -} - -function makeR (g, k, p, q) { - return g.toRed(BN.mont(p)).redPow(k).fromRed().mod(q) -} - -module.exports = sign -module.exports.getKey = getKey -module.exports.makeKey = makeKey - -},{"./curves.json":115,"bn.js":89,"browserify-rsa":112,"create-hmac":164,"elliptic":182,"parse-asn1":370,"safe-buffer":134}],118:[function(require,module,exports){ -// much of this based on https://github.com/indutny/self-signed/blob/gh-pages/lib/rsa.js -var Buffer = require('safe-buffer').Buffer -var BN = require('bn.js') -var EC = require('elliptic').ec -var parseKeys = require('parse-asn1') -var curves = require('./curves.json') - -function verify (sig, hash, key, signType, tag) { - var pub = parseKeys(key) - if (pub.type === 'ec') { - // rsa keys can be interpreted as ecdsa ones in openssl - if (signType !== 'ecdsa' && signType !== 'ecdsa/rsa') throw new Error('wrong public key type') - return ecVerify(sig, hash, pub) - } else if (pub.type === 'dsa') { - if (signType !== 'dsa') throw new Error('wrong public key type') - return dsaVerify(sig, hash, pub) - } else { - if (signType !== 'rsa' && signType !== 'ecdsa/rsa') throw new Error('wrong public key type') - } - hash = Buffer.concat([tag, hash]) - var len = pub.modulus.byteLength() - var pad = [1] - var padNum = 0 - while (hash.length + pad.length + 2 < len) { - pad.push(0xff) - padNum++ - } - pad.push(0x00) - var i = -1 - while (++i < hash.length) { - pad.push(hash[i]) - } - pad = Buffer.from(pad) - var red = BN.mont(pub.modulus) - sig = new BN(sig).toRed(red) - - sig = sig.redPow(new BN(pub.publicExponent)) - sig = Buffer.from(sig.fromRed().toArray()) - var out = padNum < 8 ? 1 : 0 - len = Math.min(sig.length, pad.length) - if (sig.length !== pad.length) out = 1 - - i = -1 - while (++i < len) out |= sig[i] ^ pad[i] - return out === 0 -} - -function ecVerify (sig, hash, pub) { - var curveId = curves[pub.data.algorithm.curve.join('.')] - if (!curveId) throw new Error('unknown curve ' + pub.data.algorithm.curve.join('.')) - - var curve = new EC(curveId) - var pubkey = pub.data.subjectPrivateKey.data - - return curve.verify(hash, sig, pubkey) -} - -function dsaVerify (sig, hash, pub) { - var p = pub.data.p - var q = pub.data.q - var g = pub.data.g - var y = pub.data.pub_key - var unpacked = parseKeys.signature.decode(sig, 'der') - var s = unpacked.s - var r = unpacked.r - checkValue(s, q) - checkValue(r, q) - var montp = BN.mont(p) - var w = s.invm(q) - var v = g.toRed(montp) - .redPow(new BN(hash).mul(w).mod(q)) - .fromRed() - .mul(y.toRed(montp).redPow(r.mul(w).mod(q)).fromRed()) - .mod(p) - .mod(q) - return v.cmp(r) === 0 -} - -function checkValue (b, q) { - if (b.cmpn(0) <= 0) throw new Error('invalid sig') - if (b.cmp(q) >= q) throw new Error('invalid sig') -} - -module.exports = verify - -},{"./curves.json":115,"bn.js":89,"elliptic":182,"parse-asn1":370,"safe-buffer":134}],119:[function(require,module,exports){ -'use strict'; - -function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } - -var codes = {}; - -function createErrorType(code, message, Base) { - if (!Base) { - Base = Error; - } - - function getMessage(arg1, arg2, arg3) { - if (typeof message === 'string') { - return message; - } else { - return message(arg1, arg2, arg3); - } - } - - var NodeError = - /*#__PURE__*/ - function (_Base) { - _inheritsLoose(NodeError, _Base); - - function NodeError(arg1, arg2, arg3) { - return _Base.call(this, getMessage(arg1, arg2, arg3)) || this; +var ToolDbNetwork = /** @class */ (function (_super) { + __extends(ToolDbNetwork, _super); + function ToolDbNetwork(db) { + var _this_1 = _super.call(this, db) || this; + _this_1._window = typeof window === "undefined" ? undefined : window; + _this_1.isNode = typeof jest !== "undefined" || typeof window === "undefined"; + _this_1.isWebWorker = typeof window === "undefined" && + typeof self !== "undefined" && + self.document === undefined; + _this_1.wss = !_this_1.isNode && !_this_1.isWebWorker && _this_1._window + ? _this_1._window.WebSocket || + _this_1._window.webkitWebSocket || + _this_1._window.mozWebSocket + : _this_1.isWebWorker + ? WebSocket + : ws_1.default; + _this_1.sockets = {}; + _this_1.socketListeners = {}; + _this_1.serverPeerData = {}; + _this_1.serversBlacklist = []; + _this_1.serversFinding = []; + _this_1.trackerUrls = defaultTrackerUrls; // .slice(0, 2); + _this_1.handledOffers = {}; + _this_1._awaitingConnections = {}; + _this_1.server = null; + // We need to create a queue to handle a situation when we need + // to contact a server, but we havent connected to it yet. + _this_1._messageQueue = []; + _this_1.removeFromAwaiting = function (pubkey) { + if (_this_1._awaitingConnections[pubkey]) { + delete _this_1._awaitingConnections[pubkey]; + } + }; + /** + * Makes a websocket connection to a tracker + */ + _this_1.makeSocket = function (url) { + return new Promise(function (resolve) { + if (!_this_1.sockets[url]) { + // this.tooldb.logger("begin tracker connection " + url); + _this_1.socketListeners[url] = _this_1.onSocketMessage; + try { + var socket_1 = new _this_1.wss(url); + _this_1.sockets[url] = socket_1; + // eslint-disable-next-line @typescript-eslint/no-this-alias + var _this = _this_1; + socket_1.onopen = function () { + // _this.tooldb.logger("tracker connected " + url); + resolve(this); + }; + socket_1.onmessage = function (e) { return _this_1.socketListeners[url](socket_1, e); }; + // eslint-disable-next-line func-names + socket_1.onerror = function () { + // removing trackers just because the error event seems like a mistake + // trackers can get disconnected and be absolutely healthy. + // const index = this.trackerUrls.indexOf(url); + // this.trackerUrls.splice(index, 1); + delete _this_1.sockets[url]; + resolve(null); + }; + socket_1.onclose = function () { + delete _this_1.sockets[url]; + // this.tooldb.logger("tracker closed " + url); + }; + } + catch (e) { + // this.tooldb.logger("makeSocket error " + url, e); + resolve(null); + } + } + else { + resolve(_this_1.sockets[url]); + } + }); + }; + /* + * Make a serverPeerData object from our keys + */ + _this_1.getServerPeerData = function () { + return new Promise(function (resolve, reject) { + var _a, _b; + if ((_a = _this_1.tooldb.options.defaultKeys) === null || _a === void 0 ? void 0 : _a.privateKey) { + (0, _1.signData)(_this_1.tooldb.options.host, (_b = _this_1.tooldb.options.defaultKeys) === null || _b === void 0 ? void 0 : _b.privateKey).then(function (signature) { + var data = { + host: _this_1.tooldb.options.host, + port: _this_1.tooldb.options.port, + ssl: _this_1.tooldb.options.ssl, + name: _this_1.tooldb.options.serverName, + pubKey: _this_1.tooldb.getPubKey(), + signature: (0, arrayBufferToHex_1.default)(signature), + }; + resolve(data); + }); + } + else { + reject(); + } + }); + }; + /** + * Announce ourselves to a tracker (send "announce") + */ + _this_1.announce = function (socket, infoHash) { return __awaiter(_this_1, void 0, void 0, function () { + var pubKey, message; + return __generator(this, function (_a) { + pubKey = this.getClientAddress(); + // this.tooldb.logger("announce", infoHash, pubKey); + if (pubKey) { + if (this.tooldb.options.server) { + this.getServerPeerData().then(function (offer) { + var offers = [0, 1, 2].map(function (n) { + return { + offer: { sdp: JSON.stringify(offer), type: "offer" }, + offer_id: (0, _1.textRandom)(20), + }; + }); + // this.tooldb.logger("announce offer", offer); + var message = { + action: "announce", + info_hash: infoHash, + peer_id: pubKey.slice(-20), + numwant: 1, + offers: offers, + }; + socket.send(JSON.stringify(message)); + }); + } + else { + message = { + action: "announce", + info_hash: infoHash, + peer_id: pubKey.slice(-20), + numwant: 1, + }; + socket.send(JSON.stringify(message)); + // this.tooldb.logger("announce message", message); + } + } + return [2 /*return*/]; + }); + }); }; + /** + * Announce ourselves to all trackers + */ + _this_1.announceAll = function () { return __awaiter(_this_1, void 0, void 0, function () { + var infoHash; + var _this_1 = this; + return __generator(this, function (_a) { + infoHash = this.codeToHash(this.tooldb.getPubKey()); + // this.tooldb.logger(`announce all start`); + this.trackerUrls.forEach(function (url, index) { return __awaiter(_this_1, void 0, void 0, function () { + var socket; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.makeSocket(url)]; + case 1: + socket = _a.sent(); + //this.tooldb.logger(" ok tracker " + url); + // this.tooldb.logger("socket", url, index); + if (socket && socket.readyState === 1) { + //this.tooldb.logger("announce to " + url); + this.announce(socket, infoHash); + } + return [2 /*return*/]; + } + }); + }); }); + return [2 /*return*/]; + }); + }); }; + /** + * Announce on trackers for a server + * Connects to it if found + */ + _this_1.findServer = function (serverKey) { return __awaiter(_this_1, void 0, void 0, function () { + var infoHash_1; + var _this_1 = this; + return __generator(this, function (_a) { + if (!this.serversFinding.includes(serverKey)) { + this.serversFinding.push(serverKey); + infoHash_1 = this.codeToHash(serverKey); + this.tooldb.logger("findServer: \"" + serverKey + "\" (" + infoHash_1 + ")"); + this.trackerUrls.forEach(function (url) { return __awaiter(_this_1, void 0, void 0, function () { + var socket; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.makeSocket(url)]; + case 1: + socket = _a.sent(); + // this.tooldb.logger(`socket:`, socket, url, socket?.readyState); + if (socket && socket.readyState === 1) { + this.announce(socket, infoHash_1); + } + return [2 /*return*/]; + } + }); + }); }); + } + return [2 /*return*/]; + }); + }); }; + _this_1.stopAnnounce = function () { + Object.values(_this_1.sockets).forEach(function (socket) { + if (socket) { + socket.terminate(); + socket.close(); + } + }); + if (_this_1.announceInterval) { + clearInterval(_this_1.announceInterval); + } + }; + /** + * Handle the tracker messages + */ + _this_1.onSocketMessage = function (socket, e) { return __awaiter(_this_1, void 0, void 0, function () { + var val, failure, serverData_1; + var _a; + return __generator(this, function (_b) { + try { + val = JSON.parse(e.data); + // this.tooldb.logger("onSocketMessage", socket.url, val); + } + catch (_e) { + this.tooldb.logger("Received malformed JSON", e.data); + return [2 /*return*/]; + } + failure = val["failure reason"]; + if (failure) { + // this.tooldb.logger(`${e.origin}: torrent tracker failure (${failure})`); + return [2 /*return*/]; + } + if (val.peer_id && val.peer_id === ((_a = this.getClientAddress()) === null || _a === void 0 ? void 0 : _a.slice(-20))) { + // this.tooldb.logger("Peer ids mismatch", val.peer_id, selfId); + return [2 /*return*/]; + } + if (val.offer && val.offer_id) { + if (this.handledOffers[val.offer_id]) { + return [2 /*return*/]; + } + this.handledOffers[val.offer_id] = true; + serverData_1 = JSON.parse(val.offer.sdp); + if (this.tooldb.serverPeers.filter(function (s) { return s.pubkey === serverData_1.pubKey; }) + .length === 0 && + !this._awaitingConnections[serverData_1.pubKey] && + !this.serverPeerData[serverData_1.pubKey] && + this.serversBlacklist.indexOf(serverData_1.pubKey) === -1) { + console.log("Now we connect to ", serverData_1); + this.connectTo(serverData_1); + } + else { + // we already connected, unplug all trackers/unsubscribe + } + return [2 /*return*/]; + } + return [2 /*return*/]; + }); + }); }; + /** + * Open a connection to a server + * @param url URL of the server (including port) + * @returns websocket + */ + _this_1.connectTo = function (serverPeer) { + _this_1.tooldb.logger("connectTo:", serverPeer); + try { + var wsUrl = serverPeer.ssl + ? "wss://" + serverPeer.host + : "ws://" + serverPeer.host + ":" + serverPeer.port; + var wss_1 = new _this_1.wss(wsUrl); + var clientId_1 = serverPeer.pubKey; + // this.serverPeers.push(serverPeer); + // Unlike other network adapters, we can just use the public key + // to identify connections. + // Therefore, we dont have to wait for a pong message to + // initialize these internal functions + _this_1.isClientConnected[serverPeer.pubKey] = function () { + return wss_1.readyState === wss_1.OPEN; + }; + _this_1.clientSocket[serverPeer.pubKey] = wss_1; + _this_1.clientToSend[serverPeer.pubKey] = function (_msg) { + wss_1.send(_msg); + }; + var previousConnection = _this_1._awaitingConnections[serverPeer.pubKey]; + if (previousConnection) { + // this.tooldb.logger("previousConnection"); + _this_1._awaitingConnections[serverPeer.pubKey].socket = wss_1; + } + else { + // this.tooldb.logger("new connection"); + _this_1._awaitingConnections[serverPeer.pubKey] = { + socket: wss_1, + tries: 0, + defer: null, + server: __assign({}, serverPeer), + }; + } + wss_1.onclose = function (_error) { + _this_1.tooldb.logger("wss.onclose", serverPeer); + _this_1.checkDisconnetion(); + if (_this_1.serversBlacklist.indexOf(serverPeer.pubKey) === -1) { + _this_1.reconnect(serverPeer.pubKey); + } + }; + wss_1.onerror = function (_error) { + var _a; + _this_1.tooldb.logger("wss.onerror", serverPeer); + _this_1.checkDisconnetion(); + if (((_a = _error === null || _error === void 0 ? void 0 : _error.error) === null || _a === void 0 ? void 0 : _a.code) !== "ETIMEDOUT" && + _this_1.serversBlacklist.indexOf(serverPeer.pubKey) === -1) { + _this_1.reconnect(serverPeer.pubKey); + } + }; + wss_1.onopen = function () { + _this_1.removeFromAwaiting(serverPeer.pubKey); + _this_1.tooldb.logger("Connected to " + serverPeer.host + ":" + serverPeer.port + " sucessfully."); + // hi peer + _this_1.craftPingMessage().then(function (msg) { + wss_1.send(msg); + }); + _this_1.serverPeerData[serverPeer.pubKey] = serverPeer; + }; + wss_1.onmessage = function (msg) { + if (!msg) { + return; + } + _this_1.onClientMessage(msg.data, clientId_1, function (id) { + clientId_1 = id; + }); + }; + return wss_1; + } + catch (e) { + _this_1.tooldb.logger("onconnect err", e); + } + return undefined; + }; + _this_1.reconnect = function (pubkey) { + var connection = _this_1._awaitingConnections[pubkey]; + if (connection) { + if (connection.defer) { + clearTimeout(connection.defer); + } + _this_1.tooldb.logger("tries: " + connection.tries); + if (connection.tries < _this_1.tooldb.options.maxRetries) { + var defer = function () { + _this_1._awaitingConnections[pubkey].tries += 1; + _this_1.tooldb.logger("connection to " + connection.server.host + ":" + connection.server.port + " retry in " + connection.tries * 2 + " seconds."); + setTimeout(function () { + _this_1.connectTo(connection.server); + }, connection.tries * 2000); + }; + connection.defer = setTimeout(defer, _this_1.tooldb.options.wait); + } + else { + _this_1.tooldb.logger("connection attempts to " + connection.server.host + ":" + connection.server.port + " exceeded,"); + _this_1.removeFromAwaiting(pubkey); + } + } + else { + _this_1.connectTo(_this_1.serverPeerData[pubkey]); + } + }; + _this_1.disconnect = function (pubKey) { + _this_1.tooldb.logger("disconnecting from " + pubKey); + var wss = _this_1.clientSocket[pubKey]; + if (wss && wss.readyState === wss.OPEN) { + wss.close(); + wss.onclose = function () { + _this_1.tooldb.logger("disconnected from " + pubKey + " sucessfully"); + }; + } + _this_1.removeFromAwaiting(pubKey); + _this_1.checkDisconnetion(); + }; + setInterval(function () { + _this_1.tryExecuteMessageQueue(); + }, 500); + // eslint-disable-next-line @typescript-eslint/no-this-alias + var _this = _this_1; + if (_this.tooldb.options.server) { + (0, waitFor_1.default)(function () { + try { + return _this.tooldb.getPubKey() !== undefined; + } + catch (_a) { + return false; + } + }).then(function () { + // Announce every 10 seconds indefinitely + setTimeout(function () { + _this.announceInterval = setInterval(_this.announceAll, 10000); + _this.announceAll(); + }, 500); + }); + } + // Basically the same as the WS network adapter + // Only for Node! + if (_this_1.tooldb.options.server && _this_1.isNode) { + _this_1.server = new ws_1.default.Server({ + port: _this_1.tooldb.options.port, + server: _this_1.tooldb.options.httpServer, + }); + _this_1.server.on("connection", function (socket) { + var clientId = null; + socket.on("close", function () { + if (clientId) { + _this_1.onClientDisconnect(clientId); + } + }); + socket.on("error", function () { + if (clientId) { + _this_1.onClientDisconnect(clientId); + } + }); + socket.on("message", function (message) { + _this_1.onClientMessage(message, clientId || "", function (id) { + clientId = id; + _this_1.isClientConnected[id] = function () { + return socket.readyState === socket.OPEN; + }; + _this_1.clientSocket[id] = socket; + _this_1.clientToSend[id] = function (_msg) { + socket.send(_msg); + }; + }); + }); + }); + } + return _this_1; } - - return NodeError; - }(Base); - - NodeError.prototype.name = Base.name; - NodeError.prototype.code = code; - codes[code] = NodeError; -} // https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js - - -function oneOf(expected, thing) { - if (Array.isArray(expected)) { - var len = expected.length; - expected = expected.map(function (i) { - return String(i); + Object.defineProperty(ToolDbNetwork.prototype, "messageQueue", { + get: function () { + return this._messageQueue; + }, + enumerable: false, + configurable: true }); + ToolDbNetwork.prototype.checkDisconnetion = function () { + if (Object.values(this.clientSocket).every(function (s) { + return s.readyState !== s.OPEN; + })) { + this.tooldb.onDisconnect(); + this.tooldb.isConnected = false; + } + }; + ToolDbNetwork.prototype.pushToMessageQueue = function (msg, to) { + this._messageQueue.push({ + message: msg, + time: Date.now(), + to: to, + }); + }; + ToolDbNetwork.prototype.codeToHash = function (code) { + return (0, _1.sha1)(code).slice(-20); + }; + ToolDbNetwork.prototype.sendToAll = function (msg, crossServerOnly) { + if (crossServerOnly === void 0) { crossServerOnly = false; } + // this.tooldb.logger("sendToAll", msg, crossServerOnly); + if (crossServerOnly) { + this.sendToAllServers(msg); + } + else { + this.pushToMessageQueue(msg, []); + this.tryExecuteMessageQueue(); + } + }; + ToolDbNetwork.prototype.sendToClientId = function (clientId, msg) { + // this.tooldb.logger("sendToClientId", clientId, msg); + this.pushToMessageQueue(msg, [clientId]); + this.tryExecuteMessageQueue(); + }; + ToolDbNetwork.prototype.sendToAllServers = function (msg) { + var _this_1 = this; + // this.tooldb.logger("sendToAllServers", msg); + var serverPeersList = this.tooldb.serverPeers + .map(function (s) { return s.pubkey; }) + .filter(function (s) { return s !== _this_1.tooldb.getPubKey(); }); + if (serverPeersList.length > 0) { + this.pushToMessageQueue(msg, serverPeersList); + } + this.tryExecuteMessageQueue(); + }; + ToolDbNetwork.prototype.tryExecuteMessageQueue = function () { + var _this_1 = this; + var sentMessageIDs = []; + var messagesToDelete = []; + var pubKey = this.getClientAddress(); + this._messageQueue.forEach(function (q) { + var message = q.message; + if (q.time + 1000 * 60 < Date.now()) { + messagesToDelete.push(message.id); + } + else { + if (pubKey && !message.to.includes(pubKey)) { + message.to.push(pubKey); + } + var finalMessageString_1 = JSON.stringify(message); + if (q.to.length > 0) { + // Send only to select clients + q.to.forEach(function (toClient) { + if (!message.to.includes(toClient) && + _this_1.isClientConnected[toClient] && + _this_1.isClientConnected[toClient]()) { + // this.tooldb.logger("Sending to client", toClient); + _this_1.clientToSend[toClient](finalMessageString_1); + if (sentMessageIDs.indexOf(message.id) === -1) { + sentMessageIDs.push(message.id); + } + } + }); + } + else { + // send to all currently connected clients + Object.keys(_this_1.clientToSend).forEach(function (toClient) { + if (!message.to.includes(toClient) && + _this_1.isClientConnected[toClient] && + _this_1.isClientConnected[toClient]()) { + _this_1.clientToSend[toClient](finalMessageString_1); + if (sentMessageIDs.indexOf(message.id) === -1) { + sentMessageIDs.push(message.id); + } + } + }); + } + } + }); + sentMessageIDs.forEach(function (id) { + var index = _this_1._messageQueue.findIndex(function (msg) { return msg.message.id === id; }); + _this_1._messageQueue.splice(index, 1); + }); + messagesToDelete.forEach(function (id) { + var index = _this_1._messageQueue.findIndex(function (msg) { return msg.message.id === id; }); + _this_1._messageQueue.splice(index, 1); + }); + }; + return ToolDbNetwork; +}(_1.ToolDbNetworkAdapter)); +exports.default = ToolDbNetwork; - if (len > 2) { - return "one of ".concat(thing, " ").concat(expected.slice(0, len - 1).join(', '), ", or ") + expected[len - 1]; - } else if (len === 2) { - return "one of ".concat(thing, " ").concat(expected[0], " or ").concat(expected[1]); - } else { - return "of ".concat(thing, " ").concat(expected[0]); +},{".":2,"./utils/arrayBufferToHex":36,"./utils/waitFor":76,"ws":207}],25:[function(require,module,exports){ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } - } else { - return "of ".concat(thing, " ").concat(String(expected)); - } -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith - - -function startsWith(str, search, pos) { - return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith - - -function endsWith(str, search, this_len) { - if (this_len === undefined || this_len > str.length) { - this_len = str.length; - } - - return str.substring(this_len - search.length, this_len) === search; -} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes - - -function includes(str, search, start) { - if (typeof start !== 'number') { - start = 0; - } - - if (start + search.length > str.length) { - return false; - } else { - return str.indexOf(search, start) !== -1; - } -} - -createErrorType('ERR_INVALID_OPT_VALUE', function (name, value) { - return 'The value "' + value + '" is invalid for option "' + name + '"'; -}, TypeError); -createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) { - // determiner: 'must be' or 'must not be' - var determiner; - - if (typeof expected === 'string' && startsWith(expected, 'not ')) { - determiner = 'must not be'; - expected = expected.replace(/^not /, ''); - } else { - determiner = 'must be'; - } - - var msg; - - if (endsWith(name, ' argument')) { - // For cases like 'first argument' - msg = "The ".concat(name, " ").concat(determiner, " ").concat(oneOf(expected, 'type')); - } else { - var type = includes(name, '.') ? 'property' : 'argument'; - msg = "The \"".concat(name, "\" ").concat(type, " ").concat(determiner, " ").concat(oneOf(expected, 'type')); - } - - msg += ". Received type ".concat(typeof actual); - return msg; -}, TypeError); -createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF'); -createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) { - return 'The ' + name + ' method is not implemented'; -}); -createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close'); -createErrorType('ERR_STREAM_DESTROYED', function (name) { - return 'Cannot call ' + name + ' after a stream was destroyed'; -}); -createErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times'); -createErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable'); -createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end'); -createErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); -createErrorType('ERR_UNKNOWN_ENCODING', function (arg) { - return 'Unknown encoding: ' + arg; -}, TypeError); -createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event'); -module.exports.codes = codes; - -},{}],120:[function(require,module,exports){ -(function (process){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// a duplex stream is just a stream that is both readable and writable. -// Since JS doesn't have multiple prototypal inheritance, this class -// prototypally inherits from Readable, and then parasitically from -// Writable. -'use strict'; -/**/ - -var objectKeys = Object.keys || function (obj) { - var keys = []; - - for (var key in obj) { - keys.push(key); - } - - return keys; }; -/**/ - - -module.exports = Duplex; - -var Readable = require('./_stream_readable'); - -var Writable = require('./_stream_writable'); - -require('inherits')(Duplex, Readable); - -{ - // Allow the keys array to be GC'ed. - var keys = objectKeys(Writable.prototype); - - for (var v = 0; v < keys.length; v++) { - var method = keys[v]; - if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; - } +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var _1 = require("."); +var proofOfWork_1 = __importDefault(require("./utils/proofOfWork")); +var signData_1 = __importDefault(require("./utils/signData")); +/** + * Triggers a PUT request to other peers. + * @param key key where we want to put the data at. + * @param value Data we want to any (any type) + * @param userNamespaced If this key bolongs to a user or its public. Making it private will enforce validation for our public key and signatures. + * @returns Promise + */ +function toolDbPut(key, value, userNamespaced) { + var _this = this; + if (userNamespaced === void 0) { userNamespaced = false; } + return new Promise(function (resolve, reject) { + if (key.includes(".")) { + // Dots are used as a delimitator character between bublic keys and the key of the user's data + reject(new Error("Key cannot include dots!; " + key)); + return; + } + if (!_this.user) { + reject(new Error("You need to log in before you can PUT.")); + return; + } + var timestamp = new Date().getTime(); + var dataString = "" + JSON.stringify(value) + _this.user.pubKey + timestamp; + // WORK + (0, proofOfWork_1.default)(dataString, _this.options.pow) + .then(function (_a) { + var _b; + var hash = _a.hash, nonce = _a.nonce; + if ((_b = _this.user) === null || _b === void 0 ? void 0 : _b.keys) { + // Sign our value + (0, signData_1.default)(hash, _this.user.keys.signKeys.privateKey) + .then(function (signature) { return __awaiter(_this, void 0, void 0, function () { + var finalKey, data, finalMessage; + var _a, _b; + return __generator(this, function (_c) { + finalKey = userNamespaced + ? ":" + ((_a = this.user) === null || _a === void 0 ? void 0 : _a.pubKey) + "." + key + : key; + data = { + k: finalKey, + p: ((_b = this.user) === null || _b === void 0 ? void 0 : _b.pubKey) || "", + n: nonce, + t: timestamp, + h: hash, + s: (0, _1.arrayBufferToBase64)(signature), + v: value, + }; + this.store.put(finalKey, JSON.stringify(data), function (err, data) { + // + }); + this.logger("PUT > " + key, data); + finalMessage = __assign({ type: "put", id: (0, _1.textRandom)(10), to: [] }, data); + this.network.sendToAll(finalMessage); + resolve(finalMessage); + return [2 /*return*/]; + }); + }); }) + .catch(reject); + } + }) + .catch(reject); + }); } +exports.default = toolDbPut; -function Duplex(options) { - if (!(this instanceof Duplex)) return new Duplex(options); - Readable.call(this, options); - Writable.call(this, options); - this.allowHalfOpen = true; - - if (options) { - if (options.readable === false) this.readable = false; - if (options.writable === false) this.writable = false; - - if (options.allowHalfOpen === false) { - this.allowHalfOpen = false; - this.once('end', onend); +},{".":2,"./utils/proofOfWork":64,"./utils/signData":68}],26:[function(require,module,exports){ +"use strict"; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } } - } -} - -Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); -Object.defineProperty(Duplex.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); -Object.defineProperty(Duplex.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; - } -}); // the no-half-open enforcer - -function onend() { - // If the writable side ended, then we're ok. - if (this._writableState.ended) return; // no more data can be written. - // But allow more writes to happen in this tick. - - process.nextTick(onEndNT, this); -} - -function onEndNT(self) { - self.end(); + return to.concat(ar || Array.prototype.slice.call(from)); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var lodash_1 = __importDefault(require("lodash")); +var _1 = require("."); +/** + * Triggers a QUERY request to other peers. + * @param key start of the key + * @param userNamespaced If this key bolongs to a user or its public. + * @param timeout Max time to wait for remote. + * @param remoteOnly Only query remote peers, do not query local store. + * @returns Promise + */ +function toolDbQueryKeys(key, userNamespaced, timeoutMs, remoteOnly) { + var _this = this; + if (userNamespaced === void 0) { userNamespaced = false; } + if (timeoutMs === void 0) { timeoutMs = 1000; } + if (remoteOnly === void 0) { remoteOnly = false; } + return new Promise(function (resolve, reject) { + var _a, _b; + if (userNamespaced && ((_a = _this.user) === null || _a === void 0 ? void 0 : _a.pubKey) === undefined) { + reject(new Error("You are not authorized yet!")); + return; + } + if (!userNamespaced && key.length < 3) { + reject(new Error("Query key is too short")); + return; + } + // if (!userNamespaced && key.startsWith(":")) { + // reject( + // new Error( + // "User namespace queries should use the userNamespaced argument" + // ) + // ); + // return; + // } + var finalKey = userNamespaced ? ":" + ((_b = _this.user) === null || _b === void 0 ? void 0 : _b.pubKey) + "." + key : key; + _this.logger("QUERY > " + finalKey); + var msgId = (0, _1.textRandom)(10); + var foundKeys = []; + var timeout; + var gotLocalKeys = remoteOnly; + if (remoteOnly === false) { + _this.store.query(finalKey).then(function (localKeys) { + gotLocalKeys = true; + foundKeys = __spreadArray(__spreadArray([], foundKeys, true), localKeys, true); + timeout = setTimeout(finishListening, timeoutMs); + }); + } + var finishListening = function () { + resolve(lodash_1.default.uniq(foundKeys)); + }; + _this.addIdListener(msgId, function (msg) { + _this.logger("QUERY RECV > " + finalKey, msg); + if (msg.type === "queryAck") { + foundKeys = __spreadArray(__spreadArray([], foundKeys, true), msg.keys, true); + if (timeout) { + clearTimeout(timeout); + } + if (gotLocalKeys === true) { + timeout = setTimeout(finishListening, timeoutMs); + } + } + }); + // Do get + _this.network.sendToAll({ + type: "query", + to: [], + key: finalKey, + id: msgId, + }); + }); } +exports.default = toolDbQueryKeys; -Object.defineProperty(Duplex.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined || this._writableState === undefined) { - return false; +},{".":2,"lodash":161}],27:[function(require,module,exports){ +"use strict"; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } } - - return this._readableState.destroyed && this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (this._readableState === undefined || this._writableState === undefined) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } -}); -}).call(this)}).call(this,require('_process')) -},{"./_stream_readable":122,"./_stream_writable":124,"_process":377,"inherits":243}],121:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// a passthrough stream. -// basically just the most minimal sort of Transform stream. -// Every written chunk gets output as-is. -'use strict'; - -module.exports = PassThrough; - -var Transform = require('./_stream_transform'); - -require('inherits')(PassThrough, Transform); - -function PassThrough(options) { - if (!(this instanceof PassThrough)) return new PassThrough(options); - Transform.call(this, options); -} - -PassThrough.prototype._transform = function (chunk, encoding, cb) { - cb(null, chunk); + return to.concat(ar || Array.prototype.slice.call(from)); }; -},{"./_stream_transform":123,"inherits":243}],122:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -'use strict'; - -module.exports = Readable; -/**/ - -var Duplex; -/**/ - -Readable.ReadableState = ReadableState; -/**/ - -var EE = require('events').EventEmitter; - -var EElistenerCount = function EElistenerCount(emitter, type) { - return emitter.listeners(type).length; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; -/**/ - -/**/ - - -var Stream = require('./internal/streams/stream'); -/**/ - - -var Buffer = require('buffer').Buffer; - -var OurUint8Array = global.Uint8Array || function () {}; - -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); -} - -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; -} -/**/ - - -var debugUtil = require('util'); - -var debug; - -if (debugUtil && debugUtil.debuglog) { - debug = debugUtil.debuglog('stream'); -} else { - debug = function debug() {}; -} -/**/ - - -var BufferList = require('./internal/streams/buffer_list'); - -var destroyImpl = require('./internal/streams/destroy'); - -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; - -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance. - - -var StringDecoder; -var createReadableStreamAsyncIterator; -var from; - -require('inherits')(Readable, Stream); - -var errorOrDestroy = destroyImpl.errorOrDestroy; -var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; - -function prependListener(emitter, event, fn) { - // Sadly this is not cacheable as some libraries bundle their own - // event emitter implementation with them. - if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any - // userland ones. NEVER DO THIS. This is here only because this code needs - // to continue to work with older versions of Node.js that do not include - // the prependListener() method. The goal is to eventually remove this hack. - - if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]]; -} - -function ReadableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream. - // These options can be provided separately as readableXXX and writableXXX. - - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to - // make all the buffer merging and length checks go away - - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer - // Note: 0 is a valid value, means "don't call _read preemptively ever" - - this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the - // linked list can remove elements from the beginning faster than - // array.shift() - - this.buffer = new BufferList(); - this.length = 0; - this.pipes = null; - this.pipesCount = 0; - this.flowing = null; - this.ended = false; - this.endEmitted = false; - this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted - // immediately, or on a later tick. We set this to true at first, because - // any actions that shouldn't happen until "later" should generally also - // not happen before the first read call. - - this.sync = true; // whenever we return null, then we set a flag to say - // that we're awaiting a 'readable' event emission. - - this.needReadable = false; - this.emittedReadable = false; - this.readableListening = false; - this.resumeScheduled = false; - this.paused = true; // Should close be emitted on destroy. Defaults to true. - - this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish') - - this.autoDestroy = !!options.autoDestroy; // has it been destroyed - - this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - - this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s - - this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled - - this.readingMore = false; - this.decoder = null; - this.encoding = null; - - if (options.encoding) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } -} - -function Readable(options) { - Duplex = Duplex || require('./_stream_duplex'); - if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside - // the ReadableState constructor, at least with V8 6.5 - - var isDuplex = this instanceof Duplex; - this._readableState = new ReadableState(options, this, isDuplex); // legacy - - this.readable = true; - - if (options) { - if (typeof options.read === 'function') this._read = options.read; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - } - - Stream.call(this); +Object.defineProperty(exports, "__esModule", { value: true }); +var lodash_1 = __importDefault(require("lodash")); +var textRandom_1 = __importDefault(require("./utils/textRandom")); +/** + * Queries the given server peer for all documents that arent present + * if our local database. + * Returns the number of documents that were synced. + * @param pubKey server public key + * @returns Promise + */ +function toolDbServerSync(pubKey) { + var _this = this; + return new Promise(function (resolve, reject) { + var localKeys = []; + _this.store.query("").then(function (keys) { + localKeys = keys; + queryAllKeys(_this, pubKey).then(function (remoteKeys) { + if (remoteKeys) { + var missingKeys_1 = lodash_1.default.difference(remoteKeys, localKeys); + if (missingKeys_1.length > 0) { + _this.logger("MISSING KEYS > ", missingKeys_1.length); + var msgId = (0, textRandom_1.default)(10); + var synced_1 = 0; + _this.addIdListener(msgId, function (msg) { + _this.logger("SERVER SYNC RECV > ", msg); + if (msg.type === "put") { + synced_1++; + } + if (synced_1 === missingKeys_1.length) { + resolve(synced_1); + } + }); + // Do get + _this.network.sendToClientId(pubKey, { + type: "get", + to: [], + key: missingKeys_1[0], + id: msgId, + }); + } + } + }); + }); + }); +} +exports.default = toolDbServerSync; +function queryAllKeys(tooldb, serverPubKey, timeoutMs) { + if (timeoutMs === void 0) { timeoutMs = 1000; } + return new Promise(function (resolve, reject) { + var _a; + if (((_a = tooldb.user) === null || _a === void 0 ? void 0 : _a.pubKey) === undefined) { + reject(new Error("You are not authorized yet!")); + return; + } + tooldb.logger("QUERY ALL > " + serverPubKey); + var msgId = (0, textRandom_1.default)(10); + var foundKeys = []; + var timeout; + var finishListening = function () { + resolve(lodash_1.default.uniq(foundKeys)); + }; + tooldb.addIdListener(msgId, function (msg) { + tooldb.logger("QUERY ALL RECV > ", msg); + if (msg.type === "queryAck") { + foundKeys = __spreadArray(__spreadArray([], foundKeys, true), msg.keys, true); + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(finishListening, timeoutMs); + } + }); + // Do get + tooldb.network.sendToClientId(serverPubKey, { + type: "query", + to: [], + key: "", + id: msgId, + }); + }); } -Object.defineProperty(Readable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._readableState === undefined) { - return false; - } - - return this._readableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._readableState) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed - - - this._readableState.destroyed = value; - } -}); -Readable.prototype.destroy = destroyImpl.destroy; -Readable.prototype._undestroy = destroyImpl.undestroy; - -Readable.prototype._destroy = function (err, cb) { - cb(err); -}; // Manually shove something into the read() buffer. -// This returns true if the highWaterMark has not been hit yet, -// similar to how Writable.write() returns true if you should -// write() some more. - - -Readable.prototype.push = function (chunk, encoding) { - var state = this._readableState; - var skipChunkCheck; - - if (!state.objectMode) { - if (typeof chunk === 'string') { - encoding = encoding || state.defaultEncoding; - - if (encoding !== state.encoding) { - chunk = Buffer.from(chunk, encoding); - encoding = ''; - } - - skipChunkCheck = true; +},{"./utils/textRandom":70,"lodash":161}],28:[function(require,module,exports){ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } - } else { - skipChunkCheck = true; - } - - return readableAddChunk(this, chunk, encoding, false, skipChunkCheck); -}; // Unshift should *always* be something directly out of read() - - -Readable.prototype.unshift = function (chunk) { - return readableAddChunk(this, chunk, null, true, false); }; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var decodeKeyString_1 = __importDefault(require("./utils/crypto/decodeKeyString")); +var decryptWithPass_1 = __importDefault(require("./utils/crypto/decryptWithPass")); +var importKey_1 = __importDefault(require("./utils/crypto/importKey")); +var base64ToUint8_1 = __importDefault(require("./utils/base64ToUint8")); +var catchReturn_1 = __importDefault(require("./utils/catchReturn")); +var fromBase64_1 = __importDefault(require("./utils/fromBase64")); +var sha256_1 = __importDefault(require("./utils/sha256")); +function toolDbSignIn(user, password) { + var _this = this; + return new Promise(function (resolve, reject) { + _this.getData("==" + user, false, 5000) + .then(function (_user) { + if (!_user) { + reject("Could not find user"); + return; + } + if ((0, sha256_1.default)(password) !== _user.pass) { + reject("Invalid password"); + return; + } + (0, decryptWithPass_1.default)((0, fromBase64_1.default)(_user.keys.skpriv), password, (0, base64ToUint8_1.default)(_user.iv)).then(function (decryptedskpriv) { + (0, decryptWithPass_1.default)((0, fromBase64_1.default)(_user.keys.ekpriv), password, (0, base64ToUint8_1.default)(_user.iv)) + .then(function (decryptedekpriv) { + var parsedKeys = __assign(__assign({}, _user.keys), { skpriv: decryptedskpriv || "", ekpriv: decryptedekpriv || "" }); + // const jsonKeys = { + // skpub: parsedKeys.skpub, + // skpriv: parsedKeys.skpriv, + // ekpub: parsedKeys.ekpub, + // ekpriv: parsedKeys.ekpriv, + // }; + // localStorage.setItem("keys", JSON.stringify(jsonKeys)); + function importKeys() { + return __awaiter(this, void 0, void 0, function () { + var skpub, skpriv, ekpub, ekpriv; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(parsedKeys.skpub), "spki", "ECDSA", ["verify"]).catch(catchReturn_1.default)]; + case 1: + skpub = _a.sent(); + return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(parsedKeys.skpriv), "pkcs8", "ECDSA", ["sign"]).catch(catchReturn_1.default)]; + case 2: + skpriv = _a.sent(); + return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(parsedKeys.ekpub), "spki", "ECDH", []).catch(catchReturn_1.default)]; + case 3: + ekpub = _a.sent(); + return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(parsedKeys.ekpriv), "pkcs8", "ECDH", ["deriveKey", "deriveBits"]).catch(catchReturn_1.default)]; + case 4: + ekpriv = _a.sent(); + return [2 /*return*/, { skpub: skpub, skpriv: skpriv, ekpub: ekpub, ekpriv: ekpriv }]; + } + }); + }); + } + importKeys() + .then(function (_a) { + var skpub = _a.skpub, skpriv = _a.skpriv, ekpub = _a.ekpub, ekpriv = _a.ekpriv; + if (!skpub || !skpriv || !ekpub || !ekpriv) { + reject(new Error("Could not import keys")); + } + else { + var newKeys = { + signKeys: { + publicKey: skpub, + privateKey: skpriv, + }, + encryptionKeys: { + publicKey: ekpub, + privateKey: ekpriv, + }, + }; + _this.user = { + keys: newKeys, + name: user, + pubKey: _user.keys.skpub, + }; + resolve(newKeys); + } + }) + .catch(catchReturn_1.default); + }) + .catch(catchReturn_1.default); + }); + }) + .catch(console.warn); + }); +} +exports.default = toolDbSignIn; -function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { - debug('readableAddChunk', chunk); - var state = stream._readableState; - - if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else { - var er; - if (!skipChunkCheck) er = chunkInvalid(state, chunk); - - if (er) { - errorOrDestroy(stream, er); - } else if (state.objectMode || chunk && chunk.length > 0) { - if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) { - chunk = _uint8ArrayToBuffer(chunk); - } - - if (addToFront) { - if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true); - } else if (state.ended) { - errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); - } else if (state.destroyed) { - return false; - } else { - state.reading = false; - - if (state.decoder && !encoding) { - chunk = state.decoder.write(chunk); - if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state); - } else { - addChunk(stream, state, chunk, false); +},{"./utils/base64ToUint8":41,"./utils/catchReturn":42,"./utils/crypto/decodeKeyString":43,"./utils/crypto/decryptWithPass":45,"./utils/crypto/importKey":54,"./utils/fromBase64":58,"./utils/sha256":67}],29:[function(require,module,exports){ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; } - } - } else if (!addToFront) { - state.reading = false; - maybeReadMore(stream, state); + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } - } // We can push more data if we are below the highWaterMark. - // Also, if we have no data yet, we can stand some more bytes. - // This is to work around cases where hwm=0, such as the repl. - - - return !state.ended && (state.length < state.highWaterMark || state.length === 0); -} - -function addChunk(stream, state, chunk, addToFront) { - if (state.flowing && state.length === 0 && !state.sync) { - state.awaitDrain = 0; - stream.emit('data', chunk); - } else { - // update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk); - if (state.needReadable) emitReadable(stream); - } - - maybeReadMore(stream, state); -} - -function chunkInvalid(state, chunk) { - var er; - - if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); - } - - return er; -} - -Readable.prototype.isPaused = function () { - return this._readableState.flowing === false; -}; // backwards compatibility. - - -Readable.prototype.setEncoding = function (enc) { - if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; - var decoder = new StringDecoder(enc); - this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8 - - this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers: - - var p = this._readableState.buffer.head; - var content = ''; - - while (p !== null) { - content += decoder.write(p.data); - p = p.next; - } - - this._readableState.buffer.clear(); - - if (content !== '') this._readableState.buffer.push(content); - this._readableState.length = content.length; - return this; -}; // Don't raise the hwm > 1GB - - -var MAX_HWM = 0x40000000; - -function computeNewHighWaterMark(n) { - if (n >= MAX_HWM) { - // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE. - n = MAX_HWM; - } else { - // Get the next highest power of 2 to prevent increasing hwm excessively in - // tiny amounts - n--; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n++; - } - - return n; -} // This function is designed to be inlinable, so please take care when making -// changes to the function body. - - -function howMuchToRead(n, state) { - if (n <= 0 || state.length === 0 && state.ended) return 0; - if (state.objectMode) return 1; - - if (n !== n) { - // Only flow one buffer at a time - if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length; - } // If we're asking for more than the current hwm, then raise the hwm. - - - if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); - if (n <= state.length) return n; // Don't have enough - - if (!state.ended) { - state.needReadable = true; - return 0; - } - - return state.length; -} // you can override either this method, or the async _read(n) below. - - -Readable.prototype.read = function (n) { - debug('read', n); - n = parseInt(n, 10); - var state = this._readableState; - var nOrig = n; - if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we - // already have a bunch of data in the buffer, then just trigger - // the 'readable' event and move on. - - if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) { - debug('read: emitReadable', state.length, state.ended); - if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this); - return null; - } - - n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up. - - if (n === 0 && state.ended) { - if (state.length === 0) endReadable(this); - return null; - } // All the actual chunk generation logic needs to be - // *below* the call to _read. The reason is that in certain - // synthetic stream cases, such as passthrough streams, _read - // may be a completely synchronous operation which may change - // the state of the read buffer, providing enough data when - // before there was *not* enough. - // - // So, the steps are: - // 1. Figure out what the state of things will be after we do - // a read from the buffer. - // - // 2. If that resulting state will trigger a _read, then call _read. - // Note that this may be asynchronous, or synchronous. Yes, it is - // deeply ugly to write APIs this way, but that still doesn't mean - // that the Readable class should behave improperly, as streams are - // designed to be sync/async agnostic. - // Take note if the _read call is sync or async (ie, if the read call - // has returned yet), so that we know whether or not it's safe to emit - // 'readable' etc. - // - // 3. Actually pull the requested chunks out of the buffer and return. - // if we need a readable event, then we need to do some reading. - - - var doRead = state.needReadable; - debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some - - if (state.length === 0 || state.length - n < state.highWaterMark) { - doRead = true; - debug('length less than watermark', doRead); - } // however, if we've ended, then there's no point, and if we're already - // reading, then it's unnecessary. - - - if (state.ended || state.reading) { - doRead = false; - debug('reading or ended', doRead); - } else if (doRead) { - debug('do read'); - state.reading = true; - state.sync = true; // if the length is currently zero, then we *need* a readable event. - - if (state.length === 0) state.needReadable = true; // call internal read method - - this._read(state.highWaterMark); - - state.sync = false; // If _read pushed data synchronously, then `reading` will be false, - // and we need to re-evaluate how much data we can return to the user. +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var _1 = require("."); +var encryptWithPass_1 = __importDefault(require("./utils/crypto/encryptWithPass")); +var generateKeysComb_1 = __importDefault(require("./utils/crypto/generateKeysComb")); +var saveKeysComb_1 = __importDefault(require("./utils/crypto/saveKeysComb")); +var generateIv_1 = __importDefault(require("./utils/generateIv")); +var proofOfWork_1 = __importDefault(require("./utils/proofOfWork")); +var sha256_1 = __importDefault(require("./utils/sha256")); +var signData_1 = __importDefault(require("./utils/signData")); +var toBase64_1 = __importDefault(require("./utils/toBase64")); +var uint8ToBase64_1 = __importDefault(require("./utils/uint8ToBase64")); +function toolDbSignUp(user, password) { + return __awaiter(this, void 0, void 0, function () { + var userRoot; + var _this = this; + return __generator(this, function (_a) { + userRoot = "==" + user; + return [2 /*return*/, new Promise(function (resolve, reject) { + _this.getData(userRoot, false, 3000) + .then(function (data) { + if (data === null) { + (0, generateKeysComb_1.default)() + .then(function (keys) { + if (keys) { + (0, saveKeysComb_1.default)(keys.signKeys, keys.encryptionKeys) + .then(function (savedKeys) { + var iv = (0, generateIv_1.default)(); + var encskpriv = ""; + var encekpriv = ""; + // Encrypt sign key + (0, encryptWithPass_1.default)(savedKeys.skpriv, password, iv) + .then(function (skenc) { + (0, encryptWithPass_1.default)(savedKeys.ekpriv, password, iv) + .then(function (ekenc) { + if (skenc) + encskpriv = skenc; + if (ekenc) + encekpriv = ekenc; + var userData = { + keys: { + skpub: savedKeys.skpub, + skpriv: (0, toBase64_1.default)(encskpriv), + ekpub: savedKeys.ekpub, + ekpriv: (0, toBase64_1.default)(encekpriv), + }, + iv: (0, uint8ToBase64_1.default)(iv), + pass: (0, sha256_1.default)(password), + }; + var timestamp = new Date().getTime(); + var userDataString = "" + JSON.stringify(userData) + savedKeys.skpub + timestamp; + (0, proofOfWork_1.default)(userDataString, 0) + .then(function (_a) { + var hash = _a.hash, nonce = _a.nonce; + (0, signData_1.default)(hash, keys.signKeys.privateKey).then(function (signature) { + var signupMessage = { + k: userRoot, + p: savedKeys.skpub, + n: nonce, + t: timestamp, + h: hash, + s: (0, _1.arrayBufferToBase64)(signature), + v: userData, + }; + _this.logger("SIGNUP PUT > " + userRoot, signupMessage); + var finalMsg = __assign({ type: "put", id: (0, _1.textRandom)(10), to: [] }, signupMessage); + _this.network.sendToAll(finalMsg); + resolve(finalMsg); + }); + }) + .catch(reject); + }) + .catch(reject); + }) + .catch(reject); + }) + .catch(function () { return reject(new Error("")); }); + } + else { + reject(new Error("Could not generate keys")); + } + }) + .catch(function () { return reject(new Error("Could not generate keys")); }); + } + else { + reject(new Error("User already exists!")); + } + }) + .catch(function () { + reject(new Error("Could not fetch user")); + }); + })]; + }); + }); +} +exports.default = toolDbSignUp; - if (!state.reading) n = howMuchToRead(nOrig, state); - } +},{".":2,"./utils/crypto/encryptWithPass":49,"./utils/crypto/generateKeysComb":53,"./utils/crypto/saveKeysComb":56,"./utils/generateIv":59,"./utils/proofOfWork":64,"./utils/sha256":67,"./utils/signData":68,"./utils/toBase64":71,"./utils/uint8ToBase64":72}],30:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var _1 = require("."); +var automerge_1 = __importDefault(require("automerge")); +/** + * Subscribe to all PUT updates for this key. + * @param key key of the data + * @param userNamespaced If this key bolongs to a user or its public. Making it private will enforce validation for our public key and signatures. + * @returns Promise + */ +function toolDbSubscribe(key, userNamespaced) { + var _this = this; + if (userNamespaced === void 0) { userNamespaced = false; } + return new Promise(function (resolve, reject) { + var _a, _b; + if (userNamespaced && ((_a = _this.user) === null || _a === void 0 ? void 0 : _a.pubKey) === undefined) { + reject(new Error("You are not authorized yet!")); + return; + } + var finalKey = userNamespaced ? ":" + ((_b = _this.user) === null || _b === void 0 ? void 0 : _b.pubKey) + "." + key : key; + _this.logger("Subscribe > " + finalKey); + var msgId = (0, _1.textRandom)(10); + _this.store.get(finalKey, function (err, data) { + if (data) { + try { + var message = JSON.parse(data); + _this.triggerKeyListener(finalKey, message); + } + catch (e) { + // do nothing + } + } + }); + // console.log("do subscribe", finalKey); + _this.loadCrdtDocument(finalKey, false).then(function (doc) { + if (doc) { + var savedDoc = automerge_1.default.save(doc); + var msg = { + type: "crdt", + key: finalKey, + id: (0, _1.textRandom)(10), + to: [], + doc: (0, _1.uint8ToBase64)(savedDoc), + }; + _this.triggerKeyListener(finalKey, msg); + } + }); + _this.network.sendToAll({ + type: "subscribe", + key: finalKey, + to: [], + id: msgId, + }); + resolve(); + }); +} +exports.default = toolDbSubscribe; - var ret; - if (n > 0) ret = fromList(n, state);else ret = null; +},{".":2,"automerge":100}],31:[function(require,module,exports){ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var _1 = require("."); +function toolDbVerificationWrapper(data) { + var _this = this; + // This wrapper function adds our custom verification functions to all messages. + return new Promise(function (resolve) { + _this.verifyMessage(data, _this.options.pow).then(function (verified) { return __awaiter(_this, void 0, void 0, function () { + var skipCustom_1; + return __generator(this, function (_a) { + if (verified) { + skipCustom_1 = true; + this._customVerificator.forEach(function (listener) { + if (listener && data.k && data.k.startsWith(listener.key)) { + skipCustom_1 = false; + listener + .fn(data) + .then(function (verified) { + if (verified) { + resolve(_1.VerifyResult.Verified); + } + else { + resolve(_1.VerifyResult.CustomVerificationFailed); + } + }) + .catch(function (e) { + resolve(_1.VerifyResult.CustomVerificationFailed); + }); + } + }); + if (skipCustom_1) { + resolve(verified); + } + } + else { + resolve(_1.VerifyResult.InvalidVerification); + } + return [2 /*return*/]; + }); + }); }); + }); +} +exports.default = toolDbVerificationWrapper; - if (ret === null) { - state.needReadable = state.length <= state.highWaterMark; - n = 0; - } else { - state.length -= n; - state.awaitDrain = 0; - } +},{".":2}],32:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.KEY_PREFIX = void 0; +var events_1 = __importDefault(require("events")); +var _1 = require("."); +var logger_1 = __importDefault(require("./logger")); +var toolDbGet_1 = __importDefault(require("./toolDbGet")); +var toolDbPut_1 = __importDefault(require("./toolDbPut")); +var toolDbSignIn_1 = __importDefault(require("./toolDbSignIn")); +var toolDbSignUp_1 = __importDefault(require("./toolDbSignUp")); +var toolDbNetwork_1 = __importDefault(require("./toolDbNetwork")); +var toolDbCrdtGet_1 = __importDefault(require("./toolDbCrdtGet")); +var toolDbCrdtPut_1 = __importDefault(require("./toolDbCrdtPut")); +var toolDbGetPubKey_1 = __importDefault(require("./toolDbGetPubKey")); +var toolDbAnonSignIn_1 = __importDefault(require("./toolDbAnonSignIn")); +var toolDbServerSync_1 = __importDefault(require("./toolDbServerSync")); +var toolDbClientOnMessage_1 = __importDefault(require("./toolDbClientOnMessage")); +var toolDbVerificationWrapper_1 = __importDefault(require("./toolDbVerificationWrapper")); +var leveldb_1 = __importDefault(require("./utils/leveldb")); +var indexedb_1 = __importDefault(require("./utils/indexedb")); +var toolDbSubscribe_1 = __importDefault(require("./toolDbSubscribe")); +var toolDbQueryKeys_1 = __importDefault(require("./toolDbQueryKeys")); +var loadCrdtDocument_1 = __importDefault(require("./loadCrdtDocument")); +var toolDbKeysSignIn_1 = __importDefault(require("./toolDbKeysSignIn")); +var handleGet_1 = __importDefault(require("./messageHandlers/handleGet")); +var handlePut_1 = __importDefault(require("./messageHandlers/handlePut")); +var handlePing_1 = __importDefault(require("./messageHandlers/handlePing")); +var handlePong_1 = __importDefault(require("./messageHandlers/handlePong")); +var handleCrdt_1 = __importDefault(require("./messageHandlers/handleCrdt")); +var handleQuery_1 = __importDefault(require("./messageHandlers/handleQuery")); +var handleCrdtGet_1 = __importDefault(require("./messageHandlers/handleCrdtGet")); +var handleCrdtPut_1 = __importDefault(require("./messageHandlers/handleCrdtPut")); +var handleSubscribe_1 = __importDefault(require("./messageHandlers/handleSubscribe")); +var arrayBufferToHex_1 = __importDefault(require("./utils/arrayBufferToHex")); +var handleFunction_1 = __importDefault(require("./messageHandlers/handleFunction")); +var toolDbFunction_1 = __importDefault(require("./toolDbFunction")); +exports.KEY_PREFIX = "3059301306072a8648ce3d020106082a8648ce3d03010703420004"; +var ToolDb = /** @class */ (function (_super) { + __extends(ToolDb, _super); + function ToolDb(options) { + if (options === void 0) { options = {}; } + var _this = _super.call(this) || this; + _this._peers = {}; + _this.logger = logger_1.default; + _this._documents = {}; + _this._pubKey = undefined; + _this.clientOnMessage = toolDbClientOnMessage_1.default; + _this.verifyMessage = _1.verifyMessage; + _this._subscriptions = []; + _this.isConnected = false; + _this._processedIds = {}; + _this._processedOutHashes = {}; + _this.subscribeData = toolDbSubscribe_1.default; + // Emitted when there are no more server peers connected to + _this.onDisconnect = function () { + // + }; + // Emitted when a server peer responds with "pong" + _this.onConnect = function () { + // + }; + _this.onPeerDisconnect = function (peerId) { + // + }; + _this.onPeerConnect = function (peerId) { + // + }; + _this.loadCrdtDocument = loadCrdtDocument_1.default; + _this.getData = toolDbGet_1.default; + _this.putData = toolDbPut_1.default; + _this.putCrdt = toolDbCrdtPut_1.default; + _this.getCrdt = toolDbCrdtGet_1.default; + _this.queryKeys = toolDbQueryKeys_1.default; + _this.doFunction = toolDbFunction_1.default; + _this.getPubKey = toolDbGetPubKey_1.default; + _this.serverSync = toolDbServerSync_1.default; + _this.signIn = toolDbSignIn_1.default; + _this.anonSignIn = toolDbAnonSignIn_1.default; + _this.keysSignIn = toolDbKeysSignIn_1.default; + _this.signUp = toolDbSignUp_1.default; + _this.verify = toolDbVerificationWrapper_1.default; + // All message handlers go here + _this.handlePing = handlePing_1.default; + _this.handlePong = handlePong_1.default; + _this.handleCrdt = handleCrdt_1.default; + _this.handleCrdtGet = handleCrdtGet_1.default; + _this.handleCrdtPut = handleCrdtPut_1.default; + _this.handleGet = handleGet_1.default; + _this.handlePut = handlePut_1.default; + _this.handleQuery = handleQuery_1.default; + _this.handleSubscribe = handleSubscribe_1.default; + _this.handleFunction = handleFunction_1.default; + /** + * id listeners listen for a specific message ID just once + */ + _this._idListeners = {}; + _this.addIdListener = function (id, fn) { + _this._idListeners[id] = fn; + }; + _this.removeIdListener = function (id) { + delete _this._idListeners[id]; + }; + /** + * Server functions allow the server to define functions to be executed by the clients + * It is up to the function creator to specify proper security on these. + * Server functions are meant to execute on data stored on the server, in a way the clients + * dont have to overload the server, use with caution! + * Custom functions are expected to be a Promise that resolves to a string, and arguments are + * passed as an array of values. Type and sanity checking is up to the developer. + */ + _this._functions = {}; + /** + * Key listeners listen for a specific key, as long as the listener remains active + */ + _this._keyListeners = []; + _this.addKeyListener = function (key, fn) { + var newListener = { + key: key, + timeout: null, + fn: fn, + }; + _this._keyListeners.push(newListener); + return _this._keyListeners.length; + }; + _this.removeKeyListener = function (id) { + var _a, _b; + if ((_a = _this._keyListeners[id]) === null || _a === void 0 ? void 0 : _a.timeout) { + clearTimeout(((_b = _this._keyListeners[id]) === null || _b === void 0 ? void 0 : _b.timeout) || undefined); + } + _this._keyListeners[id] = null; + }; + _this.triggerKeyListener = function (key, message) { + // console.warn(`triggerKeyListener ${key}`); + _this._keyListeners.forEach(function (listener) { + if ((listener === null || listener === void 0 ? void 0 : listener.key) === key) { + // console.log(`TRIGGER OK`, message); + if (listener.timeout) { + clearTimeout(listener.timeout); + } + listener.timeout = setTimeout(function () { return listener.fn(message); }, _this.options.triggerDebouce); + } + }); + }; + /** + * Custom verificators can enhance default verification on any key field + */ + _this._customVerificator = []; + _this.addCustomVerification = function (key, fn) { + var newListener = { + key: key, + fn: fn, + }; + _this._customVerificator.push(newListener); + return _this._customVerificator.length; + }; + _this.removeCustomVerification = function (id) { + _this._customVerificator[id] = null; + }; + _this.user = undefined; + _this._options = { + db: "tooldb", + maxRetries: 5, + triggerDebouce: 100, + wait: 2000, + pow: 0, + server: false, + host: "127.0.0.1", + port: 8080, + debug: false, + httpServer: undefined, + networkAdapter: toolDbNetwork_1.default, + storageName: "tooldb", + storageAdapter: typeof window === "undefined" ? leveldb_1.default : indexedb_1.default, + topic: "", + defaultKeys: undefined, + maxPeers: 4, + ssl: false, + serverName: "default-server", + }; + _this._options = __assign(__assign({}, _this.options), options); + if (!_this._options.defaultKeys) { + (0, _1.generateKeyPair)("ECDSA", false).then(function (key) { + if (key.publicKey && key.privateKey) { + _this._options.defaultKeys = key; + (0, _1.exportKey)("spki", key.publicKey).then(function (skpub) { + _this._pubKey = (0, arrayBufferToHex_1.default)(skpub); + if (_this._pubKey.startsWith(exports.KEY_PREFIX)) { + _this._pubKey = _this._pubKey.slice(exports.KEY_PREFIX.length); + } + _this.emit("init", _this._pubKey); + }); + } + }); + } + else { + (0, _1.exportKey)("spki", _this._options.defaultKeys.publicKey).then(function (skpub) { + _this._pubKey = (0, arrayBufferToHex_1.default)(skpub); + if (_this._pubKey.startsWith(exports.KEY_PREFIX)) { + _this._pubKey = _this._pubKey.slice(exports.KEY_PREFIX.length); + } + _this.emit("init", _this._pubKey); + }); + } + // These could be made to be customizable by setting the variables as public + _this._network = new _this.options.networkAdapter(_this); + _this._store = _this.options.storageAdapter(_this.options.storageName); + return _this; + } + Object.defineProperty(ToolDb.prototype, "subscriptions", { + get: function () { + return this._subscriptions; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDb.prototype, "pubKey", { + get: function () { + return this._pubKey; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDb.prototype, "processedIds", { + get: function () { + return this._processedIds; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDb.prototype, "processedOutHashes", { + get: function () { + return this._processedOutHashes; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDb.prototype, "serverPeers", { + get: function () { + return Object.values(this._peers).filter(function (peer) { return peer.isServer; }); + }, + enumerable: false, + configurable: true + }); + ToolDb.prototype.getUserNamespacedKey = function (key) { + var _a; + return ":" + (((_a = this.user) === null || _a === void 0 ? void 0 : _a.pubKey) || "") + "." + key; + }; + Object.defineProperty(ToolDb.prototype, "functions", { + get: function () { + return this._functions; + }, + enumerable: false, + configurable: true + }); + ToolDb.prototype.addServerFunction = function (functionName, fn) { + this._functions[functionName] = fn; + }; + Object.defineProperty(ToolDb.prototype, "options", { + get: function () { + return this._options; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDb.prototype, "network", { + get: function () { + return this._network; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDb.prototype, "peers", { + get: function () { + return this._peers; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDb.prototype, "store", { + get: function () { + return this._store; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(ToolDb.prototype, "documents", { + get: function () { + return this._documents; + }, + enumerable: false, + configurable: true + }); + return ToolDb; +}(events_1.default)); +exports.default = ToolDb; - if (state.length === 0) { - // If we have nothing in the buffer, then we want to know - // as soon as we *do* get something into the buffer. - if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick. +},{".":2,"./loadCrdtDocument":3,"./logger":4,"./messageHandlers/handleCrdt":5,"./messageHandlers/handleCrdtGet":6,"./messageHandlers/handleCrdtPut":7,"./messageHandlers/handleFunction":8,"./messageHandlers/handleGet":9,"./messageHandlers/handlePing":10,"./messageHandlers/handlePong":11,"./messageHandlers/handlePut":12,"./messageHandlers/handleQuery":13,"./messageHandlers/handleSubscribe":14,"./toolDbAnonSignIn":16,"./toolDbClientOnMessage":17,"./toolDbCrdtGet":18,"./toolDbCrdtPut":19,"./toolDbFunction":20,"./toolDbGet":21,"./toolDbGetPubKey":22,"./toolDbKeysSignIn":23,"./toolDbNetwork":24,"./toolDbPut":25,"./toolDbQueryKeys":26,"./toolDbServerSync":27,"./toolDbSignIn":28,"./toolDbSignUp":29,"./toolDbSubscribe":30,"./toolDbVerificationWrapper":31,"./utils/arrayBufferToHex":36,"./utils/indexedb":62,"./utils/leveldb":63,"events":395}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VerifyResult = void 0; +var VerifyResult; +(function (VerifyResult) { + VerifyResult["CustomVerificationFailed"] = "CustomVerificationFailed"; + VerifyResult["InvalidData"] = "InvalidData"; + VerifyResult["InvalidVerification"] = "InvalidVerification"; + VerifyResult["CantOverwrite"] = "CantOverwrite"; + VerifyResult["InvalidTimestamp"] = "InvalidTimestamp"; + VerifyResult["PubKeyMismatch"] = "PubKeyMismatch"; + VerifyResult["NoProofOfWork"] = "NoProofOfWork"; + VerifyResult["InvalidHashNonce"] = "InvalidHashNonce"; + VerifyResult["InvalidSignature"] = "InvalidSignature"; + VerifyResult["Verified"] = "Verified"; +})(VerifyResult = exports.VerifyResult || (exports.VerifyResult = {})); - if (nOrig !== n && state.ended) endReadable(this); - } +},{}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); - if (ret !== null) this.emit('data', ret); - return ret; +},{}],35:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; +Object.defineProperty(exports, "__esModule", { value: true }); +var arrayBufferToString_1 = __importDefault(require("./arrayBufferToString")); +var toBase64_1 = __importDefault(require("./toBase64")); +function arrayBufferToBase64(arr) { + return (0, toBase64_1.default)((0, arrayBufferToString_1.default)(arr)); +} +exports.default = arrayBufferToBase64; -function onEofChunk(stream, state) { - debug('onEofChunk'); - if (state.ended) return; - - if (state.decoder) { - var chunk = state.decoder.end(); - - if (chunk && chunk.length) { - state.buffer.push(chunk); - state.length += state.objectMode ? 1 : chunk.length; +},{"./arrayBufferToString":37,"./toBase64":71}],36:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function arrayBufferToHex(buffer) { + var data_view = new DataView(buffer); + var iii; + var len; + var hex = ""; + var c; + for (iii = 0, len = data_view.byteLength; iii < len; iii += 1) { + c = data_view.getUint8(iii).toString(16); + if (c.length < 2) { + c = "0" + c; + } + hex += c; } - } - - state.ended = true; - - if (state.sync) { - // if we are sync, wait until next tick to emit the data. - // Otherwise we risk emitting data in the flow() - // the readable code triggers during a read() call - emitReadable(stream); - } else { - // emit 'readable' now to make sure it gets picked up. - state.needReadable = false; + return hex; +} +exports.default = arrayBufferToHex; - if (!state.emittedReadable) { - state.emittedReadable = true; - emitReadable_(stream); +},{}],37:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function arrayBufferToString(arr) { + var byteArray = new Uint8Array(arr); + var byteString = ""; + for (var i = 0; i < byteArray.byteLength; i += 1) { + byteString += String.fromCodePoint(byteArray[i]); } - } -} // Don't emit readable right away in sync mode, because this can trigger -// another read() call => stack overflow. This way, it might trigger -// a nextTick recursion warning, but that's not so bad. - - -function emitReadable(stream) { - var state = stream._readableState; - debug('emitReadable', state.needReadable, state.emittedReadable); - state.needReadable = false; - - if (!state.emittedReadable) { - debug('emitReadable', state.flowing); - state.emittedReadable = true; - process.nextTick(emitReadable_, stream); - } + return byteString; } +exports.default = arrayBufferToString; -function emitReadable_(stream) { - var state = stream._readableState; - debug('emitReadable_', state.destroyed, state.length, state.ended); - - if (!state.destroyed && (state.length || state.ended)) { - stream.emit('readable'); - state.emittedReadable = false; - } // The stream needs another readable event if - // 1. It is not flowing, as the flow mechanism will take - // care of it. - // 2. It is not ended. - // 3. It is below the highWaterMark, so we can schedule - // another readable later. - - - state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark; - flow(stream); -} // at this point, the user has presumably seen the 'readable' event, -// and called read() to consume some data. that may have triggered -// in turn another _read(n) call, in which case reading = true if -// it's in progress. -// However, if we're not ended, or reading, and the length < hwm, -// then go ahead and try to read some more preemptively. - - -function maybeReadMore(stream, state) { - if (!state.readingMore) { - state.readingMore = true; - process.nextTick(maybeReadMore_, stream, state); - } +},{}],38:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var fromBase64_1 = __importDefault(require("./fromBase64")); +var stringToArrayBuffer_1 = __importDefault(require("./stringToArrayBuffer")); +function base64ToArrayBuffer(str) { + return (0, stringToArrayBuffer_1.default)((0, fromBase64_1.default)(str)); } +exports.default = base64ToArrayBuffer; -function maybeReadMore_(stream, state) { - // Attempt to read more data if we should. - // - // The conditions for reading more data are (one of): - // - Not enough data buffered (state.length < state.highWaterMark). The loop - // is responsible for filling the buffer with enough data if such data - // is available. If highWaterMark is 0 and we are not in the flowing mode - // we should _not_ attempt to buffer any extra data. We'll get more data - // when the stream consumer calls read() instead. - // - No data in the buffer, and the stream is in flowing mode. In this mode - // the loop below is responsible for ensuring read() is called. Failing to - // call read here would abort the flow and there's no other mechanism for - // continuing the flow if the stream consumer has just subscribed to the - // 'data' event. - // - // In addition to the above conditions to keep reading data, the following - // conditions prevent the data from being read: - // - The stream has ended (state.ended). - // - There is already a pending 'read' operation (state.reading). This is a - // case where the the stream has called the implementation defined _read() - // method, but they are processing the call asynchronously and have _not_ - // called push() with new data. In this case we skip performing more - // read()s. The execution ends in this method again after the _read() ends - // up calling push() with more data. - while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) { - var len = state.length; - debug('maybeReadMore read 0'); - stream.read(0); - if (len === state.length) // didn't get any data, stop spinning. - break; - } - - state.readingMore = false; -} // abstract method. to be overridden in specific implementation classes. -// call cb(er, data) where data is <= n in length. -// for virtual (non-string, non-buffer) streams, "length" is somewhat -// arbitrary, and perhaps not very meaningful. - - -Readable.prototype._read = function (n) { - errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()')); +},{"./fromBase64":58,"./stringToArrayBuffer":69}],39:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; - -Readable.prototype.pipe = function (dest, pipeOpts) { - var src = this; - var state = this._readableState; - - switch (state.pipesCount) { - case 0: - state.pipes = dest; - break; - - case 1: - state.pipes = [state.pipes, dest]; - break; - - default: - state.pipes.push(dest); - break; - } - - state.pipesCount += 1; - debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); - var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr; - var endFn = doEnd ? onend : unpipe; - if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn); - dest.on('unpipe', onunpipe); - - function onunpipe(readable, unpipeInfo) { - debug('onunpipe'); - - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); - } - } - } - - function onend() { - debug('onend'); - dest.end(); - } // when the dest drains, it reduces the awaitDrain counter - // on the source. This would be more elegant with a .once() - // handler in flow(), but adding and removing repeatedly is - // too slow. - - - var ondrain = pipeOnDrain(src); - dest.on('drain', ondrain); - var cleanedUp = false; - - function cleanup() { - debug('cleanup'); // cleanup event handlers once the pipe is broken - - dest.removeListener('close', onclose); - dest.removeListener('finish', onfinish); - dest.removeListener('drain', ondrain); - dest.removeListener('error', onerror); - dest.removeListener('unpipe', onunpipe); - src.removeListener('end', onend); - src.removeListener('end', unpipe); - src.removeListener('data', ondata); - cleanedUp = true; // if the reader is waiting for a drain event from this - // specific writer, then it would cause it to never start - // flowing again. - // So, if this is awaiting a drain, then we just call it now. - // If we don't know, then assume that we are waiting for one. - - if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain(); - } - - src.on('data', ondata); - - function ondata(chunk) { - debug('ondata'); - var ret = dest.write(chunk); - debug('dest.write', ret); - - if (ret === false) { - // If the user unpiped during `dest.write()`, it is possible - // to get stuck in a permanently paused state if that write - // also returned false. - // => Check whether `dest` is still a piping destination. - if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) { - debug('false write response, pause', state.awaitDrain); - state.awaitDrain++; - } - - src.pause(); +Object.defineProperty(exports, "__esModule", { value: true }); +var fromBase64_1 = __importDefault(require("./fromBase64")); +function base64ToBinaryChange(based) { + var str = (0, fromBase64_1.default)(based); + var buf = new ArrayBuffer(str.length); + var bufView = new Uint8Array(buf); + for (var i = 0, strLen = str.length; i < strLen; i += 1) { + bufView[i] = str.charCodeAt(i); } - } // if the dest has an error, then stop piping into it. - // however, don't suppress the throwing behavior for this. - - - function onerror(er) { - debug('onerror', er); - unpipe(); - dest.removeListener('error', onerror); - if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er); - } // Make sure our error handler is attached before userland ones. - - - prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once. - - function onclose() { - dest.removeListener('finish', onfinish); - unpipe(); - } - - dest.once('close', onclose); - - function onfinish() { - debug('onfinish'); - dest.removeListener('close', onclose); - unpipe(); - } - - dest.once('finish', onfinish); - - function unpipe() { - debug('unpipe'); - src.unpipe(dest); - } // tell the dest that it's being piped to - - - dest.emit('pipe', src); // start the flow if it hasn't been started already. - - if (!state.flowing) { - debug('pipe resume'); - src.resume(); - } + bufView.__binaryChange = true; + return bufView; +} +exports.default = base64ToBinaryChange; - return dest; +},{"./fromBase64":58}],40:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; - -function pipeOnDrain(src) { - return function pipeOnDrainFunctionResult() { - var state = src._readableState; - debug('pipeOnDrain', state.awaitDrain); - if (state.awaitDrain) state.awaitDrain--; - - if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) { - state.flowing = true; - flow(src); +Object.defineProperty(exports, "__esModule", { value: true }); +var fromBase64_1 = __importDefault(require("./fromBase64")); +function base64ToBinaryDocument(based) { + var str = (0, fromBase64_1.default)(based); + var buf = new ArrayBuffer(str.length); + var bufView = new Uint8Array(buf); + for (var i = 0, strLen = str.length; i < strLen; i += 1) { + bufView[i] = str.charCodeAt(i); } - }; + bufView.__binaryDocument = true; + return bufView; } +exports.default = base64ToBinaryDocument; -Readable.prototype.unpipe = function (dest) { - var state = this._readableState; - var unpipeInfo = { - hasUnpiped: false - }; // if we're not piping anywhere, then do nothing. - - if (state.pipesCount === 0) return this; // just one destination. most common case. - - if (state.pipesCount === 1) { - // passed in one, but it's not the right one. - if (dest && dest !== state.pipes) return this; - if (!dest) dest = state.pipes; // got a match. - - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - if (dest) dest.emit('unpipe', this, unpipeInfo); - return this; - } // slow case. multiple pipe destinations. - - - if (!dest) { - // remove all. - var dests = state.pipes; - var len = state.pipesCount; - state.pipes = null; - state.pipesCount = 0; - state.flowing = false; - - for (var i = 0; i < len; i++) { - dests[i].emit('unpipe', this, { - hasUnpiped: false - }); +},{"./fromBase64":58}],41:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var fromBase64_1 = __importDefault(require("./fromBase64")); +function base64ToUint8(based) { + var str = (0, fromBase64_1.default)(based); + var buf = new ArrayBuffer(str.length); + var bufView = new Uint8Array(buf); + for (var i = 0, strLen = str.length; i < strLen; i += 1) { + bufView[i] = str.charCodeAt(i); } + return bufView; +} +exports.default = base64ToUint8; - return this; - } // try to find the right one. - - - var index = indexOf(state.pipes, dest); - if (index === -1) return this; - state.pipes.splice(index, 1); - state.pipesCount -= 1; - if (state.pipesCount === 1) state.pipes = state.pipes[0]; - dest.emit('unpipe', this, unpipeInfo); - return this; -}; // set up data events if they are asked for -// Ensure readable listeners eventually get something - - -Readable.prototype.on = function (ev, fn) { - var res = Stream.prototype.on.call(this, ev, fn); - var state = this._readableState; - - if (ev === 'data') { - // update readableListening so that resume() may be a no-op - // a few lines down. This is needed to support once('readable'). - state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused - - if (state.flowing !== false) this.resume(); - } else if (ev === 'readable') { - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.flowing = false; - state.emittedReadable = false; - debug('on readable', state.length, state.reading); - - if (state.length) { - emitReadable(this); - } else if (!state.reading) { - process.nextTick(nReadingNextTick, this); - } - } - } +},{"./fromBase64":58}],42:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function catchReturn(arg) { + // console.error(arg); + return undefined; +} +exports.default = catchReturn; - return res; +},{}],43:[function(require,module,exports){ +(function (global){(function (){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; +Object.defineProperty(exports, "__esModule", { value: true }); +var stringToArrayBuffer_1 = __importDefault(require("../stringToArrayBuffer")); +function decodeKeyString(keydataB64) { + var keydataS = global.atob(keydataB64); + var keydata = (0, stringToArrayBuffer_1.default)(keydataS); + return keydata; +} +exports.default = decodeKeyString; -Readable.prototype.addListener = Readable.prototype.on; - -Readable.prototype.removeListener = function (ev, fn) { - var res = Stream.prototype.removeListener.call(this, ev, fn); - - if (ev === 'readable') { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } - - return res; +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../stringToArrayBuffer":69}],44:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +var arrayBufferToString_1 = __importDefault(require("../arrayBufferToString")); +var base64ToUint8_1 = __importDefault(require("../base64ToUint8")); +var catchReturn_1 = __importDefault(require("../catchReturn")); +var stringToArrayBuffer_1 = __importDefault(require("../stringToArrayBuffer")); +function decryptData(data, privateKey, iv) { + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle + .decrypt({ + name: "AES-GCM", + iv: (0, base64ToUint8_1.default)(iv), + tagLength: 128, // The tagLength you used to encrypt (if any) + }, privateKey, // from generateKey or importKey above + (0, stringToArrayBuffer_1.default)(data) // ArrayBuffer of the data + ) + .then(function (decrypted) { + // returns an ArrayBuffer containing the decrypted data + return (0, arrayBufferToString_1.default)(decrypted); + }) + .catch(catchReturn_1.default); +} +exports.default = decryptData; -Readable.prototype.removeAllListeners = function (ev) { - var res = Stream.prototype.removeAllListeners.apply(this, arguments); - - if (ev === 'readable' || ev === undefined) { - // We need to check if there is someone still listening to - // readable and reset the state. However this needs to happen - // after readable has been emitted but before I/O (nextTick) to - // support once('readable', fn) cycles. This means that calling - // resume within the same tick will have no - // effect. - process.nextTick(updateReadableListening, this); - } +},{"../../getCrypto":1,"../arrayBufferToString":37,"../base64ToUint8":41,"../catchReturn":42,"../stringToArrayBuffer":69}],45:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var arrayBufferToString_1 = __importDefault(require("../arrayBufferToString")); +var catchReturn_1 = __importDefault(require("../catchReturn")); +var generateKeyFromPassword_1 = __importDefault(require("./generateKeyFromPassword")); +var stringToArrayBuffer_1 = __importDefault(require("../stringToArrayBuffer")); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +function decryptWithPass(data, password, vector) { + var crypto = (0, getCrypto_1.default)(); + return (0, generateKeyFromPassword_1.default)(password) + .then(function (keyObject) { + return crypto.subtle + .decrypt({ name: "AES-GCM", iv: vector }, keyObject, (0, stringToArrayBuffer_1.default)(data)) + .then(function (result) { return (0, arrayBufferToString_1.default)(result); }) + .catch(catchReturn_1.default); + }) + .catch(catchReturn_1.default); +} +exports.default = decryptWithPass; - return res; +},{"../../getCrypto":1,"../arrayBufferToString":37,"../catchReturn":42,"../stringToArrayBuffer":69,"./generateKeyFromPassword":51}],46:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; - -function updateReadableListening(self) { - var state = self._readableState; - state.readableListening = self.listenerCount('readable') > 0; - - if (state.resumeScheduled && !state.paused) { - // flowing needs to be set to true now, otherwise - // the upcoming resume will not flow. - state.flowing = true; // crude way to check if we should resume - } else if (self.listenerCount('data') > 0) { - self.resume(); - } +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +function deriveSecret(keys) { + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle.deriveKey({ + name: "ECDH", + public: keys.publicKey, + }, keys.privateKey, { + name: "AES-GCM", + length: 256, + }, true, ["encrypt", "decrypt"]); } +exports.default = deriveSecret; -function nReadingNextTick(self) { - debug('readable nexttick read 0'); - self.read(0); -} // pause() and resume() are remnants of the legacy readable stream API -// If the user uses them, then switch into old mode. - - -Readable.prototype.resume = function () { - var state = this._readableState; - - if (!state.flowing) { - debug('resume'); // we flow only if there is no one listening - // for readable, but we still have to call - // resume() - - state.flowing = !state.readableListening; - resume(this, state); - } - - state.paused = false; - return this; +},{"../../getCrypto":1}],47:[function(require,module,exports){ +(function (global){(function (){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; - -function resume(stream, state) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - process.nextTick(resume_, stream, state); - } +Object.defineProperty(exports, "__esModule", { value: true }); +var arrayBufferToString_1 = __importDefault(require("../arrayBufferToString")); +function encodeKeyString(keydata) { + var keydataS = (0, arrayBufferToString_1.default)(keydata); + var keydataB64 = global.btoa(keydataS); + return keydataB64; } +exports.default = encodeKeyString; -function resume_(stream, state) { - debug('resume', state.reading); - - if (!state.reading) { - stream.read(0); - } - - state.resumeScheduled = false; - stream.emit('resume'); - flow(stream); - if (state.flowing && !state.reading) stream.read(0); +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../arrayBufferToString":37}],48:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +var arrayBufferToString_1 = __importDefault(require("../arrayBufferToString")); +var stringToArrayBuffer_1 = __importDefault(require("../stringToArrayBuffer")); +function encryptData(data, publicKey, iv) { + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle + .encrypt({ + name: "AES-GCM", + // Don't re-use initialization vectors! + // Always generate a new iv every time your encrypt! + // Recommended to use 12 bytes length + iv: iv, + // Tag length (optional) + tagLength: 128, // can be 32, 64, 96, 104, 112, 120 or 128 (default) + }, publicKey, // from generateKey or importKey above + (0, stringToArrayBuffer_1.default)(data) // ArrayBuffer of data you want to encrypt + ) + .then(function (encrypted) { + // returns an ArrayBuffer containing the encrypted data + return (0, arrayBufferToString_1.default)(encrypted); + }) + .catch(console.error); } +exports.default = encryptData; -Readable.prototype.pause = function () { - debug('call pause flowing=%j', this._readableState.flowing); - - if (this._readableState.flowing !== false) { - debug('pause'); - this._readableState.flowing = false; - this.emit('pause'); - } - - this._readableState.paused = true; - return this; +},{"../../getCrypto":1,"../arrayBufferToString":37,"../stringToArrayBuffer":69}],49:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; +Object.defineProperty(exports, "__esModule", { value: true }); +var arrayBufferToString_1 = __importDefault(require("../arrayBufferToString")); +var catchReturn_1 = __importDefault(require("../catchReturn")); +var generateKeyFromPassword_1 = __importDefault(require("./generateKeyFromPassword")); +var stringToArrayBuffer_1 = __importDefault(require("../stringToArrayBuffer")); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +function encryptWithPass(secretmessage, password, vector) { + var crypto = (0, getCrypto_1.default)(); + return (0, generateKeyFromPassword_1.default)(password) + .then(function (keyObject) { + // encrypt promise + return crypto.subtle + .encrypt({ name: "AES-GCM", iv: vector }, keyObject, (0, stringToArrayBuffer_1.default)(secretmessage)) + .then(function (result) { + return (0, arrayBufferToString_1.default)(result); + }) + .catch(catchReturn_1.default); + }) + .catch(catchReturn_1.default); +} +exports.default = encryptWithPass; -function flow(stream) { - var state = stream._readableState; - debug('flow', state.flowing); - - while (state.flowing && stream.read() !== null) { - ; - } -} // wrap an old-style stream as the async data source. -// This is *not* part of the readable stream interface. -// It is an ugly unfortunate mess of history. - +},{"../../getCrypto":1,"../arrayBufferToString":37,"../catchReturn":42,"../stringToArrayBuffer":69,"./generateKeyFromPassword":51}],50:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +function exportKey(format, key) { + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle.exportKey(format, // can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only) + key // can be a publicKey or privateKey, as long as extractable was true, + ); +} +exports.default = exportKey; -Readable.prototype.wrap = function (stream) { - var _this = this; +},{"../../getCrypto":1}],51:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +var stringToArrayBuffer_1 = __importDefault(require("../stringToArrayBuffer")); +function generateKeyFromPassword(password) { + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle + .importKey("raw", (0, stringToArrayBuffer_1.default)(password), { name: "PBKDF2" }, false, ["deriveKey"]) + .then(function (importedPassword) { + return crypto.subtle.deriveKey({ + name: "PBKDF2", + salt: (0, stringToArrayBuffer_1.default)("t6sa@8d7!2ñs?=adjq2ng"), + iterations: 100000, + hash: "SHA-256", + }, importedPassword, { + name: "AES-GCM", + length: 128, + }, false, ["encrypt", "decrypt"]); + }); +} +exports.default = generateKeyFromPassword; - var state = this._readableState; - var paused = false; - stream.on('end', function () { - debug('wrapped end'); +},{"../../getCrypto":1,"../stringToArrayBuffer":69}],52:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +function generateKeyPair(mode, extractable) { + if (extractable === void 0) { extractable = false; } + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle.generateKey({ + name: mode, + namedCurve: "P-256", // can be "P-256", "P-384", or "P-521" + }, extractable, // whether the key is extractable (i.e. can be used in exportKey) + mode === "ECDSA" ? ["sign", "verify"] : ["deriveKey", "deriveBits"]); +} +exports.default = generateKeyPair; - if (state.decoder && !state.ended) { - var chunk = state.decoder.end(); - if (chunk && chunk.length) _this.push(chunk); +},{"../../getCrypto":1}],53:[function(require,module,exports){ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var generateKeyPair_1 = __importDefault(require("./generateKeyPair")); +function generateKeysComb() { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, new Promise(function (resolve) { + return (0, generateKeyPair_1.default)("ECDSA", true).then(function (signKeys) { + return (0, generateKeyPair_1.default)("ECDH", true).then(function (encryptionKeys) { + resolve({ + signKeys: signKeys, + encryptionKeys: encryptionKeys, + }); + }); + }); + })]; + }); + }); +} +exports.default = generateKeysComb; - _this.push(null); - }); - stream.on('data', function (chunk) { - debug('wrapped data'); - if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode - - if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return; - - var ret = _this.push(chunk); +},{"./generateKeyPair":52}],54:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +// JsonWebKey +function importKey( +// eslint-disable-next-line no-undef +key, type, algorithm, +// eslint-disable-next-line no-undef +ops) { + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle.importKey(type, // can be "jwk" (public or private), "spki" (public only), or "pkcs8" (private only) + key, { + name: algorithm, + namedCurve: "P-256", + }, true, // whether the key is extractable (i.e. can be used in exportKey) + ops // "verify" for public key import, "sign" for private key imports + ); +} +exports.default = importKey; - if (!ret) { - paused = true; - stream.pause(); +},{"../../getCrypto":1}],55:[function(require,module,exports){ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } - }); // proxy all the other methods. - // important when wrapping filters and duplexes. +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var catchReturn_1 = __importDefault(require("../catchReturn")); +var decodeKeyString_1 = __importDefault(require("./decodeKeyString")); +var importKey_1 = __importDefault(require("./importKey")); +function importKeys(parsedKeys) { + return __awaiter(this, void 0, void 0, function () { + var skpub, skpriv, ekpub, ekpriv; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(parsedKeys.skpub), "spki", "ECDSA", ["verify"]).catch(catchReturn_1.default)]; + case 1: + skpub = _a.sent(); + return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(parsedKeys.skpriv), "pkcs8", "ECDSA", ["sign"]).catch(catchReturn_1.default)]; + case 2: + skpriv = _a.sent(); + return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(parsedKeys.ekpub), "spki", "ECDH", []).catch(catchReturn_1.default)]; + case 3: + ekpub = _a.sent(); + return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(parsedKeys.ekpriv), "pkcs8", "ECDH", ["deriveKey", "deriveBits"]).catch(catchReturn_1.default)]; + case 4: + ekpriv = _a.sent(); + return [2 /*return*/, { skpub: skpub, skpriv: skpriv, ekpub: ekpub, ekpriv: ekpriv }]; + } + }); + }); +} +function loadKeysComb(parsedKeys) { + return new Promise(function (resolve, reject) { + importKeys(parsedKeys).then(function (_a) { + var skpub = _a.skpub, skpriv = _a.skpriv, ekpub = _a.ekpub, ekpriv = _a.ekpriv; + if (!skpub || !skpriv || !ekpub || !ekpriv) { + reject(new Error("Could not import keys")); + } + else { + resolve({ + signKeys: { + publicKey: skpub, + privateKey: skpriv, + }, + encryptionKeys: { + publicKey: ekpub, + privateKey: ekpriv, + }, + }); + } + }); + }); +} +exports.default = loadKeysComb; - for (var i in stream) { - if (this[i] === undefined && typeof stream[i] === 'function') { - this[i] = function methodWrap(method) { - return function methodWrapReturnFunction() { - return stream[method].apply(stream, arguments); - }; - }(i); +},{"../catchReturn":42,"./decodeKeyString":43,"./importKey":54}],56:[function(require,module,exports){ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } - } // proxy certain important events. - - - for (var n = 0; n < kProxyEvents.length; n++) { - stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n])); - } // when we try to consume some more bytes, simply unpause the - // underlying stream. - +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var encodeKeyString_1 = __importDefault(require("./encodeKeyString")); +var exportKey_1 = __importDefault(require("./exportKey")); +function saveKeysComb(signKeys, encryptionKeys) { + return __awaiter(this, void 0, void 0, function () { + var skpub, skpriv, ekpub, ekpriv, jsonKeys; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, (0, exportKey_1.default)("spki", signKeys.publicKey)]; + case 1: + skpub = _a.sent(); + return [4 /*yield*/, (0, exportKey_1.default)("pkcs8", signKeys.privateKey)]; + case 2: + skpriv = _a.sent(); + return [4 /*yield*/, (0, exportKey_1.default)("spki", encryptionKeys.publicKey)]; + case 3: + ekpub = _a.sent(); + return [4 /*yield*/, (0, exportKey_1.default)("pkcs8", encryptionKeys.privateKey)]; + case 4: + ekpriv = _a.sent(); + jsonKeys = { + skpub: (0, encodeKeyString_1.default)(skpub), + skpriv: (0, encodeKeyString_1.default)(skpriv), + ekpub: (0, encodeKeyString_1.default)(ekpub), + ekpriv: (0, encodeKeyString_1.default)(ekpriv), + }; + // ONLY FOR TESTING! NEVER ENABLE IN PROD + // localStorage.setItem("keys", JSON.stringify(jsonKeys)); + return [2 /*return*/, jsonKeys]; + } + }); + }); +} +exports.default = saveKeysComb; - this._read = function (n) { - debug('wrapped _read', n); +},{"./encodeKeyString":47,"./exportKey":50}],57:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../../getCrypto")); +var stringToArrayBuffer_1 = __importDefault(require("../stringToArrayBuffer")); +function verifyData(data, signature, publicKey, hashName) { + if (hashName === void 0) { hashName = "SHA-256"; } + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle.verify({ + name: "ECDSA", + hash: { name: hashName }, // can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" + }, publicKey, // from generateKey or importKey above + signature, // ArrayBuffer of the signature + (0, stringToArrayBuffer_1.default)(data) // ArrayBuffer of the data + ); +} +exports.default = verifyData; - if (paused) { - paused = false; - stream.resume(); - } - }; +},{"../../getCrypto":1,"../stringToArrayBuffer":69}],58:[function(require,module,exports){ +(function (global){(function (){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function fromBase64(str) { + return decodeURIComponent(escape(global.atob(str))); +} +exports.default = fromBase64; - return this; +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],59:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; - -if (typeof Symbol === 'function') { - Readable.prototype[Symbol.asyncIterator] = function () { - if (createReadableStreamAsyncIterator === undefined) { - createReadableStreamAsyncIterator = require('./internal/streams/async_iterator'); - } - - return createReadableStreamAsyncIterator(this); - }; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../getCrypto")); +function generateIv() { + var crypto = (0, getCrypto_1.default)(); + return crypto.getRandomValues(new Uint8Array(12)); } +exports.default = generateIv; -Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.highWaterMark; - } -}); -Object.defineProperty(Readable.prototype, 'readableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState && this._readableState.buffer; - } -}); -Object.defineProperty(Readable.prototype, 'readableFlowing', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.flowing; - }, - set: function set(state) { - if (this._readableState) { - this._readableState.flowing = state; - } - } -}); // exposed for testing purposes only. - -Readable._fromList = fromList; -Object.defineProperty(Readable.prototype, 'readableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._readableState.length; - } -}); // Pluck off n bytes from an array of buffers. -// Length is the combined lengths of all the buffers in the list. -// This function is designed to be inlinable, so please take care when making -// changes to the function body. - -function fromList(n, state) { - // nothing buffered - if (state.length === 0) return null; - var ret; - if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) { - // read it all, truncate the list - if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length); - state.buffer.clear(); - } else { - // read part of list - ret = state.buffer.consume(n, state.decoder); - } - return ret; +},{"../getCrypto":1}],60:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var __1 = require(".."); +function getPeerSignature(privateKey, topic, timestamp, host, port) { + var dataToSign = (0, __1.sha256)(topic + "-" + timestamp + "-" + host + ":" + port); + return (0, __1.signData)(dataToSign, privateKey, "SHA-1").then(__1.arrayBufferToBase64); } +exports.default = getPeerSignature; -function endReadable(stream) { - var state = stream._readableState; - debug('endReadable', state.endEmitted); - - if (!state.endEmitted) { - state.ended = true; - process.nextTick(endReadableNT, state, stream); - } +},{"..":2}],61:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function hexToArrayBuffer(hex) { + var pairs = hex.toUpperCase().match(/[\dA-F]{2}/gi); + if (!pairs) + return new Uint8Array(); + // convert the octets to integers + var integers = pairs.map(function (s) { + return parseInt(s, 16); + }); + var array = new Uint8Array(integers); + return array.buffer; } +exports.default = hexToArrayBuffer; -function endReadableNT(state, stream) { - debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift. - - if (!state.endEmitted && state.length === 0) { - state.endEmitted = true; - stream.readable = false; - stream.emit('end'); - - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the writable side is ready for autoDestroy as well - var wState = stream._writableState; - - if (!wState || wState.autoDestroy && wState.finished) { - stream.destroy(); - } - } - } +},{}],62:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function indexedb(dbName) { + if (dbName === void 0) { dbName = "tooldb"; } + var db = null; + var store = { + start: function () { + // + }, + put: function (key, data, callback) { + // + }, + get: function (key, callback) { + // + }, + query: function (key) { return Promise.resolve([]); }, + quit: function () { + // + }, + }; + store.start = function () { + // console.warn("store start"); + var open = indexedDB.open(dbName, 1); + open.onupgradeneeded = function (eve) { + eve.target.result.createObjectStore(dbName); + }; + open.onsuccess = function () { + db = open.result; + }; + open.onerror = function (eve) { + console.warn(eve || 1); + }; + }; + store.start(); + store.quit = function () { + // console.warn("store quit"); + if (db) { + db.close(); + db = null; + } + }; + store.put = function (key, data, cb) { + // console.warn("store put", key); + if (!db) { + setTimeout(function () { + store.put(key, data, cb); + }, 1); + return; + } + var tx = db.transaction([dbName], "readwrite"); + var obj = tx.objectStore(dbName); + var req = obj.put(data, "" + key); + if (cb) { + req.onsuccess = + obj.onsuccess = + tx.onsuccess = + function () { + cb(false); + }; + req.onabort = + obj.onabort = + tx.onabort = + function (eve) { + cb(eve || "put.tx.abort"); + }; + req.onerror = + obj.onerror = + tx.onerror = + function (eve) { + cb(eve || "put.tx.error"); + }; + } + }; + store.get = function (key, cb) { + // console.warn("store get", key); + if (!db) { + setTimeout(function () { + store.get(key, cb); + }, 9); + return; + } + var tx = db.transaction([dbName], "readonly"); + var obj = tx.objectStore(dbName); + var req = obj.get("" + key); + req.onsuccess = function () { + cb(false, req.result); + }; + req.onabort = function (eve) { + cb(eve || 4); + }; + req.onerror = function (eve) { + cb(eve || 5); + }; + }; + store.query = function (key) { + return new Promise(function (resolve, reject) { + try { + var keyRange = IDBKeyRange.bound(key, key + "|", true, true); + var tx = db.transaction([dbName], "readonly"); + var obj = tx.objectStore(dbName); + var keysArray_1 = []; + obj.openCursor(keyRange).onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + keysArray_1.push(event.target.result.key); + cursor.continue(); + } + else { + resolve(keysArray_1); + } + }; + } + catch (error) { + reject(error); + } + }); + }; + setInterval(function () { + db && db.close(); + db = null; + store.start(); + }, 1000 * 15); // reset webkit bug? + return store; } +exports.default = indexedb; -if (typeof Symbol === 'function') { - Readable.from = function (iterable, opts) { - if (from === undefined) { - from = require('./internal/streams/from'); - } - - return from(Readable, iterable, opts); - }; +},{}],63:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function leveldb(dbName) { + if (dbName === void 0) { dbName = "tooldb"; } + var level = require("level"); + var db = null; + var store = { + start: function () { + // + }, + put: function (key, data, callback) { + // + }, + get: function (key, callback) { + // + }, + query: function (key) { return Promise.resolve([]); }, + quit: function () { + // + }, + }; + store.start = function () { + db = level(dbName); + }; + store.start(); + store.quit = function () { + db.close(); + }; + store.put = function (key, data, cb) { + db.put(key, data, function (err) { + if (err) { + if (cb) + cb(err); + } + else { + if (cb) + cb(false); + } + }); + }; + store.get = function (key, cb) { + // console.warn("store get", key); + if (!db) { + setTimeout(function () { + store.get(key, cb); + }, 9); + return; + } + db.get(key, function (err, value) { + if (err) { + if (cb) + cb(err); + } + else { + if (cb) + cb(false, value); + } + }); + }; + store.query = function (key) { + return new Promise(function (resolve, reject) { + try { + var array_1 = []; + db.createKeyStream({ + gte: key, + lte: key + "\uffff", + }) + .on("data", function (data) { + // if (data.startsWith(key)) { + array_1.push(data); + // } + }) + .on("error", function (err) { + reject(err); + }) + .on("close", function () { + resolve(array_1); + }); + } + catch (error) { + reject(error); + } + }); + }; + return store; } +exports.default = leveldb; -function indexOf(xs, x) { - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) return i; - } - - return -1; +},{"level":156}],64:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var sha256_1 = __importDefault(require("./sha256")); +function proofOfWork(value, difficulty) { + return new Promise(function (resolve) { + var nonce = 0; + var hash = (0, sha256_1.default)("" + value + nonce); + while (hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")) { + nonce += 1; + hash = (0, sha256_1.default)("" + value + nonce); + } + resolve({ nonce: nonce, hash: hash }); + }); } -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../errors":119,"./_stream_duplex":120,"./internal/streams/async_iterator":125,"./internal/streams/buffer_list":126,"./internal/streams/destroy":127,"./internal/streams/from":129,"./internal/streams/state":131,"./internal/streams/stream":132,"_process":377,"buffer":135,"events":201,"inherits":243,"string_decoder/":400,"util":91}],123:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// a transform stream is a readable/writable stream where you do -// something with the data. Sometimes it's called a "filter", -// but that's not a great name for it, since that implies a thing where -// some bits pass through, and others are simply ignored. (That would -// be a valid example of a transform, of course.) -// -// While the output is causally related to the input, it's not a -// necessarily symmetric or synchronous transformation. For example, -// a zlib stream might take multiple plain-text writes(), and then -// emit a single compressed chunk some time in the future. -// -// Here's how this works: -// -// The Transform stream has all the aspects of the readable and writable -// stream classes. When you write(chunk), that calls _write(chunk,cb) -// internally, and returns false if there's a lot of pending writes -// buffered up. When you call read(), that calls _read(n) until -// there's enough pending readable data buffered up. -// -// In a transform stream, the written data is placed in a buffer. When -// _read(n) is called, it transforms the queued up data, calling the -// buffered _write cb's as it consumes chunks. If consuming a single -// written chunk would result in multiple output chunks, then the first -// outputted bit calls the readcb, and subsequent chunks just go into -// the read buffer, and will cause it to emit 'readable' if necessary. -// -// This way, back-pressure is actually determined by the reading side, -// since _read has to be called to start processing a new chunk. However, -// a pathological inflate type of transform can cause excessive buffering -// here. For example, imagine a stream where every byte of input is -// interpreted as an integer from 0-255, and then results in that many -// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in -// 1kb of data being output. In this case, you could write a very small -// amount of input, and end up with a very large amount of output. In -// such a pathological inflating mechanism, there'd be no way to tell -// the system to stop doing the transform. A single 4MB write could -// cause the system to run out of memory. -// -// However, even in such a pathological case, only a single written chunk -// would be consumed, and then the rest would wait (un-transformed) until -// the results of the previous transformed chunk were consumed. -'use strict'; - -module.exports = Transform; - -var _require$codes = require('../errors').codes, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, - ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING, - ERR_TRANSFORM_WITH_LENGTH_0 = _require$codes.ERR_TRANSFORM_WITH_LENGTH_0; - -var Duplex = require('./_stream_duplex'); - -require('inherits')(Transform, Duplex); - -function afterTransform(er, data) { - var ts = this._transformState; - ts.transforming = false; - var cb = ts.writecb; - - if (cb === null) { - return this.emit('error', new ERR_MULTIPLE_CALLBACK()); - } - - ts.writechunk = null; - ts.writecb = null; - if (data != null) // single equals check for both `null` and `undefined` - this.push(data); - cb(er); - var rs = this._readableState; - rs.reading = false; +exports.default = proofOfWork; - if (rs.needReadable || rs.length < rs.highWaterMark) { - this._read(rs.highWaterMark); - } +},{"./sha256":67}],65:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var animals = [ + "Pug", + "Chicken", + "Slug", + "Snail", + "Pig", + "Cow", + "Sheep", + "Horse", + "Squirrel", + "Turtle", + "Unicorn", + "Dragon", + "Dolphin", + "Cat", + "Chow Chow", + "Elephant", + "Meerkat", + "Polar Bear", + "Bear", + "Rabbit", + "Koala", + "Parrot", + "Raven", + "Frog", + "Rat", + "Mouse", + "Bee", + "Tiger", + "Lion", + "Giraffe", + "Ant", + "Spider", + "Zebra", +]; +function randomAnimal() { + return animals[Math.round(Math.random() * animals.length)]; } +exports.default = randomAnimal; -function Transform(options) { - if (!(this instanceof Transform)) return new Transform(options); - Duplex.call(this, options); - this._transformState = { - afterTransform: afterTransform.bind(this), - needTransform: false, - transforming: false, - writecb: null, - writechunk: null, - writeencoding: null - }; // start out asking for a readable event once data is transformed. - - this._readableState.needReadable = true; // we have implemented the _read method, and done the other things - // that Readable wants before the first _read call, so unset the - // sync guard flag. - - this._readableState.sync = false; - - if (options) { - if (typeof options.transform === 'function') this._transform = options.transform; - if (typeof options.flush === 'function') this._flush = options.flush; - } // When the writable side finishes, then flush out anything remaining. - - - this.on('prefinish', prefinish); +},{}],66:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var crypto_1 = __importDefault(require("crypto")); +function sha1(str, hex) { + if (hex === void 0) { hex = true; } + var hash = crypto_1.default.createHash("sha1"); + hash.update(str); + return hash.digest(hex ? "hex" : "base64"); } +exports.default = sha1; -function prefinish() { - var _this = this; - - if (typeof this._flush === 'function' && !this._readableState.destroyed) { - this._flush(function (er, data) { - done(_this, er, data); - }); - } else { - done(this, null, null); - } +},{"crypto":365}],67:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var crypto_1 = __importDefault(require("crypto")); +function sha256(str, hex) { + if (hex === void 0) { hex = true; } + var hash = crypto_1.default.createHash("sha256"); + hash.update(str); + return hash.digest(hex ? "hex" : "base64"); } +exports.default = sha256; -Transform.prototype.push = function (chunk, encoding) { - this._transformState.needTransform = false; - return Duplex.prototype.push.call(this, chunk, encoding); -}; // This is the part where you do stuff! -// override this function in implementation classes. -// 'chunk' is an input chunk. -// -// Call `push(newChunk)` to pass along transformed output -// to the readable side. You may call 'push' zero or more times. -// -// Call `cb(err)` when you are done with this chunk. If you pass -// an error, then that'll put the hurt on the whole operation. If you -// never call cb(), then you'll never get another chunk. - - -Transform.prototype._transform = function (chunk, encoding, cb) { - cb(new ERR_METHOD_NOT_IMPLEMENTED('_transform()')); +},{"crypto":365}],68:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; +Object.defineProperty(exports, "__esModule", { value: true }); +var getCrypto_1 = __importDefault(require("../getCrypto")); +var stringToArrayBuffer_1 = __importDefault(require("./stringToArrayBuffer")); +function signData(data, privateKey, hashName) { + if (hashName === void 0) { hashName = "SHA-256"; } + var crypto = (0, getCrypto_1.default)(); + return crypto.subtle.sign({ + name: "ECDSA", + hash: { name: hashName }, // can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" + }, privateKey, // from generateKey or importKey above + (0, stringToArrayBuffer_1.default)(data) // ArrayBuffer of data you want to sign + ); +} +exports.default = signData; -Transform.prototype._write = function (chunk, encoding, cb) { - var ts = this._transformState; - ts.writecb = cb; - ts.writechunk = chunk; - ts.writeencoding = encoding; - - if (!ts.transforming) { - var rs = this._readableState; - if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark); - } -}; // Doesn't matter what the args are here. -// _transform does all the work. -// That we got here means that the readable side wants more data. - - -Transform.prototype._read = function (n) { - var ts = this._transformState; +},{"../getCrypto":1,"./stringToArrayBuffer":69}],69:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function stringToArrayBuffer(str) { + var buf = new ArrayBuffer(str.length); + var bufView = new Uint8Array(buf); + for (var i = 0, strLen = str.length; i < strLen; i += 1) { + bufView[i] = str.charCodeAt(i); + } + return buf; +} +exports.default = stringToArrayBuffer; - if (ts.writechunk !== null && !ts.transforming) { - ts.transforming = true; +},{}],70:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function textRandom(_l, _c) { + if (_l === void 0) { _l = 24; } + var l = _l; + var s = ""; + var c = _c || "0123456789ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz"; + while (l > 0) { + s += c.charAt(Math.floor(Math.random() * c.length)); + l -= 1; + } + return s; +} +exports.default = textRandom; - this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); - } else { - // mark that we need a transform, so that any data that comes in - // will get processed, now that we've asked for it. - ts.needTransform = true; - } -}; +},{}],71:[function(require,module,exports){ +(function (global){(function (){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function toBase64(str) { + return global.btoa(unescape(encodeURIComponent(str))); +} +exports.default = toBase64; -Transform.prototype._destroy = function (err, cb) { - Duplex.prototype._destroy.call(this, err, function (err2) { - cb(err2); - }); +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],72:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; - -function done(stream, er, data) { - if (er) return stream.emit('error', er); - if (data != null) // single equals check for both `null` and `undefined` - stream.push(data); // TODO(BridgeAR): Write a test for these two error cases - // if there's nothing in the write buffer, then that means - // that nothing more will ever be provided - - if (stream._writableState.length) throw new ERR_TRANSFORM_WITH_LENGTH_0(); - if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING(); - return stream.push(null); +Object.defineProperty(exports, "__esModule", { value: true }); +var toBase64_1 = __importDefault(require("./toBase64")); +function uint8ToBase64(byteArray) { + var byteString = ""; + for (var i = 0; i < byteArray.byteLength; i += 1) { + byteString += String.fromCodePoint(byteArray[i]); + } + return (0, toBase64_1.default)(byteString); } -},{"../errors":119,"./_stream_duplex":120,"inherits":243}],124:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -// A bit simpler than readable streams. -// Implement an async ._write(chunk, encoding, cb), and it'll handle all -// the drain event emission and buffering. -'use strict'; - -module.exports = Writable; -/* */ - -function WriteReq(chunk, encoding, cb) { - this.chunk = chunk; - this.encoding = encoding; - this.callback = cb; - this.next = null; -} // It seems a linked list but it is not -// there will be only 2 of these for each stream - - -function CorkedRequest(state) { - var _this = this; - - this.next = null; - this.entry = null; +exports.default = uint8ToBase64; - this.finish = function () { - onCorkedFinish(_this, state); - }; +},{"./toBase64":71}],73:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function uniq(array) { + return Array.from(new Set(array)); } -/* */ - -/**/ - - -var Duplex; -/**/ - -Writable.WritableState = WritableState; -/**/ +exports.default = uniq; -var internalUtil = { - deprecate: require('util-deprecate') +},{}],74:[function(require,module,exports){ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); }; -/**/ - -/**/ - -var Stream = require('./internal/streams/stream'); -/**/ - - -var Buffer = require('buffer').Buffer; - -var OurUint8Array = global.Uint8Array || function () {}; - -function _uint8ArrayToBuffer(chunk) { - return Buffer.from(chunk); +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var sha256_1 = __importDefault(require("./sha256")); +var message_1 = require("../types/message"); +var decodeKeyString_1 = __importDefault(require("./crypto/decodeKeyString")); +var importKey_1 = __importDefault(require("./crypto/importKey")); +var verifyData_1 = __importDefault(require("./crypto/verifyData")); +var __1 = require(".."); +/** + * Verifies a message validity (PoW, pubKey, timestamp, signatures) + * @param msg AnyMessage + * @param pow amount of proof of work required, number of leading zeroes (default is 0/no pow) + * @returns boolean or undefined if the message type does not match + */ +function verifyMessage(msg, pow) { + if (pow === void 0) { pow = 0; } + return __awaiter(this, void 0, void 0, function () { + var strData, publicKeyNamespace, key_1, data, pubKeyString, pubKey, verified; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + strData = JSON.stringify(msg.v); + if (msg.t === undefined || + msg.k === undefined || + msg.h === undefined || + msg.p === undefined || + msg.s === undefined) { + return [2 /*return*/, message_1.VerifyResult.InvalidData]; + } + // Max clock shift allowed is 30 seconds. + // Ten seconds was my original threshold but it failed some times. + if (msg.t > new Date().getTime() + 30000) { + // console.warn("Invalid message timestamp."); + return [2 /*return*/, message_1.VerifyResult.InvalidTimestamp]; + } + publicKeyNamespace = false; + if (msg.k.slice(0, 1) == ":") { + publicKeyNamespace = msg.k.split(".")[0].slice(1); + } + if (!(msg.k.slice(0, 2) == "==")) return [3 /*break*/, 2]; + key_1 = msg.k; + return [4 /*yield*/, new Promise(function (resolve) { + _this.store.get(key_1, function (err, data) { + if (data) { + try { + var message = JSON.parse(data); + resolve(message); + } + catch (e) { + resolve(null); + } + } + else { + resolve(null); + } + }); + })]; + case 1: + data = _a.sent(); + if (data && data.p !== msg.p) + return [2 /*return*/, message_1.VerifyResult.CantOverwrite]; + _a.label = 2; + case 2: + pubKeyString = msg.p; + if (publicKeyNamespace && publicKeyNamespace !== pubKeyString) { + // console.warn("Provided pub keys do not match"); + return [2 /*return*/, message_1.VerifyResult.PubKeyMismatch]; + } + // Verify hash and nonce (adjust zeroes for difficulty of the network) + // While this POW does not enforce security per-se, it does make it harder + // for attackers to spam the network, and could be adjusted by peers. + // Disabled for now because it is painful on large requests + if (pow > 0) { + if (msg.h.slice(0, pow) !== new Array(pow).fill("0").join("")) { + // console.warn("No valid hash (no pow)"); + return [2 /*return*/, message_1.VerifyResult.NoProofOfWork]; + } + if ((0, sha256_1.default)("" + strData + pubKeyString + msg.t + msg.n) !== msg.h) { + // console.warn("Specified hash does not generate a valid pow"); + return [2 /*return*/, message_1.VerifyResult.InvalidHashNonce]; + } + } + return [4 /*yield*/, (0, importKey_1.default)((0, decodeKeyString_1.default)(pubKeyString), "spki", "ECDSA", ["verify"])]; + case 3: + pubKey = _a.sent(); + return [4 /*yield*/, (0, verifyData_1.default)(msg.h, (0, __1.base64ToArrayBuffer)(msg.s), pubKey)]; + case 4: + verified = _a.sent(); + // console.warn(`Signature validation: ${verified ? "Sucess" : "Failed"}`); + return [2 /*return*/, verified ? message_1.VerifyResult.Verified : message_1.VerifyResult.InvalidSignature]; + } + }); + }); } +exports.default = verifyMessage; -function _isUint8Array(obj) { - return Buffer.isBuffer(obj) || obj instanceof OurUint8Array; +},{"..":2,"../types/message":33,"./crypto/decodeKeyString":43,"./crypto/importKey":54,"./crypto/verifyData":57,"./sha256":67}],75:[function(require,module,exports){ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var __1 = require(".."); +var tooldb_1 = require("../tooldb"); +var hexToArrayBuffer_1 = __importDefault(require("./hexToArrayBuffer")); +function verifyPeer(peer) { + // Import the public key string + return (0, __1.importKey)((0, hexToArrayBuffer_1.default)(tooldb_1.KEY_PREFIX + peer.pubkey), "spki", "ECDSA", ["verify"]).then(function (pubKey) { + return (0, __1.verifyData)((0, __1.sha256)(peer.topic + "-" + peer.timestamp + "-" + peer.host + ":" + peer.port), (0, __1.base64ToArrayBuffer)(peer.sig), pubKey, "SHA-1"); + }); } +exports.default = verifyPeer; -var destroyImpl = require('./internal/streams/destroy'); - -var _require = require('./internal/streams/state'), - getHighWaterMark = _require.getHighWaterMark; - -var _require$codes = require('../errors').codes, - ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE, - ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, - ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK, - ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE, - ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED, - ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES, - ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END, - ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING; - -var errorOrDestroy = destroyImpl.errorOrDestroy; - -require('inherits')(Writable, Stream); - -function nop() {} - -function WritableState(options, stream, isDuplex) { - Duplex = Duplex || require('./_stream_duplex'); - options = options || {}; // Duplex streams are both readable and writable, but share - // the same options object. - // However, some cases require setting options to different - // values for the readable and the writable sides of the duplex stream, - // e.g. options.readableObjectMode vs. options.writableObjectMode, etc. - - if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream - // contains buffers or objects. - - this.objectMode = !!options.objectMode; - if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false - // Note: 0 is a valid value, means that we always return false if - // the entire buffer is not flushed immediately on write() - - this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called - - this.finalCalled = false; // drain event flag. - - this.needDrain = false; // at the start of calling end() - - this.ending = false; // when end() has been called, and returned - - this.ended = false; // when 'finish' is emitted - - this.finished = false; // has it been destroyed - - this.destroyed = false; // should we decode strings into buffers before passing to _write? - // this is here so that some node-core streams can optimize string - // handling at a lower level. - - var noDecode = options.decodeStrings === false; - this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string - // encoding is 'binary' so we have to make this configurable. - // Everything else in the universe uses 'utf8', though. - - this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement - // of how much we're waiting to get pushed to some underlying - // socket or file. +},{"..":2,"../tooldb":32,"./hexToArrayBuffer":61}],76:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function waitFor(fn, interval, timeout) { + if (interval === void 0) { interval = 100; } + if (timeout === void 0) { timeout = 30000; } + return new Promise(function (resolve, reject) { + var intervalFn = setInterval(function () { + if (fn()) { + clearInterval(intervalFn); + clearTimeout(timeoutFn); + resolve(); + } + }, interval); + var timeoutFn = setTimeout(function () { + clearInterval(intervalFn); + reject(); + }, timeout); + }); +} +exports.default = waitFor; - this.length = 0; // a flag to see when we're in the middle of a write. +},{}],77:[function(require,module,exports){ +'use strict' - this.writing = false; // when true all writes will be buffered until .uncork() call +const emptyOptions = Object.freeze({}) - this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately, - // or on a later tick. We set this to true at first, because any - // actions that shouldn't happen until "later" should generally also - // not happen before the first write call. +function AbstractChainedBatch (db) { + if (typeof db !== 'object' || db === null) { + throw new TypeError('First argument must be an abstract-leveldown compliant store') + } - this.sync = true; // a flag to know if we're processing previously buffered items, which - // may call the _write() callback in the same tick, so that we don't - // end up in an overlapped onwrite situation. + this.db = db + this._operations = [] + this._written = false +} - this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb) +AbstractChainedBatch.prototype._checkWritten = function () { + if (this._written) { + throw new Error('write() already called on this batch') + } +} - this.onwrite = function (er) { - onwrite(stream, er); - }; // the callback that the user supplies to write(chunk,encoding,cb) +AbstractChainedBatch.prototype.put = function (key, value, options) { + this._checkWritten() + const err = this.db._checkKey(key) || this.db._checkValue(value) + if (err) throw err - this.writecb = null; // the amount that is being written when _write is called. + key = this.db._serializeKey(key) + value = this.db._serializeValue(value) - this.writelen = 0; - this.bufferedRequest = null; - this.lastBufferedRequest = null; // number of pending user-supplied write callbacks - // this must be 0 before 'finish' can be emitted + this._put(key, value, options != null ? options : emptyOptions) - this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs - // This is relevant for synchronous Transform streams + return this +} - this.prefinished = false; // True if the error was already emitted and should not be thrown again +AbstractChainedBatch.prototype._put = function (key, value, options) { + this._operations.push({ ...options, type: 'put', key, value }) +} - this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true. +AbstractChainedBatch.prototype.del = function (key, options) { + this._checkWritten() - this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end') + const err = this.db._checkKey(key) + if (err) throw err - this.autoDestroy = !!options.autoDestroy; // count buffered requests + key = this.db._serializeKey(key) + this._del(key, options != null ? options : emptyOptions) - this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always - // one allocated and free to use, and we maintain at most two + return this +} - this.corkedRequestsFree = new CorkedRequest(this); +AbstractChainedBatch.prototype._del = function (key, options) { + this._operations.push({ ...options, type: 'del', key }) } -WritableState.prototype.getBuffer = function getBuffer() { - var current = this.bufferedRequest; - var out = []; +AbstractChainedBatch.prototype.clear = function () { + this._checkWritten() + this._clear() - while (current) { - out.push(current); - current = current.next; - } + return this +} - return out; -}; +AbstractChainedBatch.prototype._clear = function () { + this._operations = [] +} -(function () { - try { - Object.defineProperty(WritableState.prototype, 'buffer', { - get: internalUtil.deprecate(function writableStateBufferGetter() { - return this.getBuffer(); - }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003') - }); - } catch (_) {} -})(); // Test _writableState for inheritance to account for Duplex streams, -// whose prototype chain only points to Readable. +AbstractChainedBatch.prototype.write = function (options, callback) { + this._checkWritten() + if (typeof options === 'function') { + callback = options + } + if (typeof callback !== 'function') { + throw new Error('write() requires a callback argument') + } + if (typeof options !== 'object' || options === null) { + options = {} + } -var realHasInstance; + this._written = true + this._write(options, callback) +} -if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') { - realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { - value: function value(object) { - if (realHasInstance.call(this, object)) return true; - if (this !== Writable) return false; - return object && object._writableState instanceof WritableState; - } - }); -} else { - realHasInstance = function realHasInstance(object) { - return object instanceof this; - }; +AbstractChainedBatch.prototype._write = function (options, callback) { + this.db._batch(this._operations, options, callback) } -function Writable(options) { - Duplex = Duplex || require('./_stream_duplex'); // Writable ctor is applied to Duplexes, too. - // `realHasInstance` is necessary because using plain `instanceof` - // would return false, as no `_writableState` property is attached. - // Trying to use the custom `instanceof` for Writable here will also break the - // Node.js LazyTransform implementation, which has a non-trivial getter for - // `_writableState` that would lead to infinite recursion. - // Checking for a Stream.Duplex instance is faster here instead of inside - // the WritableState constructor, at least with V8 6.5 +// Expose browser-compatible nextTick for dependents +AbstractChainedBatch.prototype._nextTick = require('./next-tick') - var isDuplex = this instanceof Duplex; - if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options); - this._writableState = new WritableState(options, this, isDuplex); // legacy. +module.exports = AbstractChainedBatch - this.writable = true; +},{"./next-tick":82}],78:[function(require,module,exports){ +'use strict' - if (options) { - if (typeof options.write === 'function') this._write = options.write; - if (typeof options.writev === 'function') this._writev = options.writev; - if (typeof options.destroy === 'function') this._destroy = options.destroy; - if (typeof options.final === 'function') this._final = options.final; +function AbstractIterator (db) { + if (typeof db !== 'object' || db === null) { + throw new TypeError('First argument must be an abstract-leveldown compliant store') } - Stream.call(this); -} // Otherwise people can pipe Writable streams, which is just wrong. + this.db = db + this._ended = false + this._nexting = false +} +AbstractIterator.prototype.next = function (callback) { + // In callback mode, we return `this` + let ret = this -Writable.prototype.pipe = function () { - errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); -}; + if (callback === undefined) { + ret = new Promise(function (resolve, reject) { + callback = function (err, key, value) { + if (err) reject(err) + else if (key === undefined && value === undefined) resolve() + else resolve([key, value]) + } + }) + } else if (typeof callback !== 'function') { + throw new Error('next() requires a callback argument') + } -function writeAfterEnd(stream, cb) { - var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb + if (this._ended) { + this._nextTick(callback, new Error('cannot call next() after end()')) + return ret + } - errorOrDestroy(stream, er); - process.nextTick(cb, er); -} // Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. + if (this._nexting) { + this._nextTick(callback, new Error('cannot call next() before previous next() has completed')) + return ret + } + this._nexting = true + this._next((err, ...rest) => { + this._nexting = false + callback(err, ...rest) + }) -function validChunk(stream, state, chunk, cb) { - var er; + return ret +} - if (chunk === null) { - er = new ERR_STREAM_NULL_VALUES(); - } else if (typeof chunk !== 'string' && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); - } +AbstractIterator.prototype._next = function (callback) { + this._nextTick(callback) +} - if (er) { - errorOrDestroy(stream, er); - process.nextTick(cb, er); - return false; +AbstractIterator.prototype.seek = function (target) { + if (this._ended) { + throw new Error('cannot call seek() after end()') + } + if (this._nexting) { + throw new Error('cannot call seek() before next() has completed') } - return true; + target = this.db._serializeKey(target) + this._seek(target) } -Writable.prototype.write = function (chunk, encoding, cb) { - var state = this._writableState; - var ret = false; +AbstractIterator.prototype._seek = function (target) {} - var isBuf = !state.objectMode && _isUint8Array(chunk); +AbstractIterator.prototype.end = function (callback) { + let promise - if (isBuf && !Buffer.isBuffer(chunk)) { - chunk = _uint8ArrayToBuffer(chunk); + if (callback === undefined) { + promise = new Promise(function (resolve, reject) { + callback = function (err) { + if (err) reject(err) + else resolve() + } + }) + } else if (typeof callback !== 'function') { + throw new Error('end() requires a callback argument') } - if (typeof encoding === 'function') { - cb = encoding; - encoding = null; + if (this._ended) { + this._nextTick(callback, new Error('end() already called on iterator')) + return promise } - if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding; - if (typeof cb !== 'function') cb = nop; - if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb); - } - return ret; -}; + this._ended = true + this._end(callback) -Writable.prototype.cork = function () { - this._writableState.corked++; -}; + return promise +} -Writable.prototype.uncork = function () { - var state = this._writableState; +AbstractIterator.prototype._end = function (callback) { + this._nextTick(callback) +} - if (state.corked) { - state.corked--; - if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state); +AbstractIterator.prototype[Symbol.asyncIterator] = async function * () { + try { + let kv + + while ((kv = (await this.next())) !== undefined) { + yield kv + } + } finally { + if (!this._ended) await this.end() } -}; +} -Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === 'string') encoding = encoding.toLowerCase(); - if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding); - this._writableState.defaultEncoding = encoding; - return this; -}; +// Expose browser-compatible nextTick for dependents +AbstractIterator.prototype._nextTick = require('./next-tick') -Object.defineProperty(Writable.prototype, 'writableBuffer', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState && this._writableState.getBuffer(); - } -}); +module.exports = AbstractIterator -function decodeChunk(state, chunk, encoding) { - if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - } +},{"./next-tick":82}],79:[function(require,module,exports){ +'use strict' - return chunk; +const supports = require('level-supports') +const isBuffer = require('is-buffer') +const catering = require('catering') +const AbstractIterator = require('./abstract-iterator') +const AbstractChainedBatch = require('./abstract-chained-batch') +const getCallback = require('./lib/common').getCallback +const getOptions = require('./lib/common').getOptions + +const hasOwnProperty = Object.prototype.hasOwnProperty +const rangeOptions = ['lt', 'lte', 'gt', 'gte'] + +function AbstractLevelDOWN (manifest) { + this.status = 'new' + + // TODO (next major): make this mandatory + this.supports = supports(manifest, { + status: true + }) } -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.highWaterMark; - } -}); // if we're already writing something, then just put this -// in the queue, and wait our turn. Otherwise, call _write -// If we return false, then we need a drain event, so set that flag. +AbstractLevelDOWN.prototype.open = function (options, callback) { + const oldStatus = this.status -function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { - if (!isBuf) { - var newChunk = decodeChunk(state, chunk, encoding); + if (typeof options === 'function') callback = options - if (chunk !== newChunk) { - isBuf = true; - encoding = 'buffer'; - chunk = newChunk; - } + if (typeof callback !== 'function') { + throw new Error('open() requires a callback argument') } - var len = state.objectMode ? 1 : chunk.length; - state.length += len; - var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false. - - if (!ret) state.needDrain = true; + if (typeof options !== 'object' || options === null) options = {} - if (state.writing || state.corked) { - var last = state.lastBufferedRequest; - state.lastBufferedRequest = { - chunk: chunk, - encoding: encoding, - isBuf: isBuf, - callback: cb, - next: null - }; + options.createIfMissing = options.createIfMissing !== false + options.errorIfExists = !!options.errorIfExists - if (last) { - last.next = state.lastBufferedRequest; - } else { - state.bufferedRequest = state.lastBufferedRequest; + this.status = 'opening' + this._open(options, (err) => { + if (err) { + this.status = oldStatus + return callback(err) } + this.status = 'open' + callback() + }) +} - state.bufferedRequestCount += 1; - } else { - doWrite(stream, state, false, len, chunk, encoding, cb); +AbstractLevelDOWN.prototype._open = function (options, callback) { + this._nextTick(callback) +} + +AbstractLevelDOWN.prototype.close = function (callback) { + const oldStatus = this.status + + if (typeof callback !== 'function') { + throw new Error('close() requires a callback argument') } - return ret; + this.status = 'closing' + this._close((err) => { + if (err) { + this.status = oldStatus + return callback(err) + } + this.status = 'closed' + callback() + }) } -function doWrite(stream, state, writev, len, chunk, encoding, cb) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite); - state.sync = false; +AbstractLevelDOWN.prototype._close = function (callback) { + this._nextTick(callback) } -function onwriteError(stream, state, sync, er, cb) { - --state.pendingcb; +AbstractLevelDOWN.prototype.get = function (key, options, callback) { + if (typeof options === 'function') callback = options - if (sync) { - // defer the callback if we are being called synchronously - // to avoid piling up things on the stack - process.nextTick(cb, er); // this can emit finish, and it will always happen - // after error + if (typeof callback !== 'function') { + throw new Error('get() requires a callback argument') + } - process.nextTick(finishMaybe, stream, state); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); - } else { - // the caller expect this to happen before if - // it is async - cb(er); - stream._writableState.errorEmitted = true; - errorOrDestroy(stream, er); // this can emit finish, but finish must - // always follow error + const err = this._checkKey(key) + if (err) return this._nextTick(callback, err) - finishMaybe(stream, state); - } + key = this._serializeKey(key) + + if (typeof options !== 'object' || options === null) options = {} + + options.asBuffer = options.asBuffer !== false + + this._get(key, options, callback) } -function onwriteStateUpdate(state) { - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; +AbstractLevelDOWN.prototype._get = function (key, options, callback) { + this._nextTick(function () { callback(new Error('NotFound')) }) } -function onwrite(stream, er) { - var state = stream._writableState; - var sync = state.sync; - var cb = state.writecb; - if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK(); - onwriteStateUpdate(state); - if (er) onwriteError(stream, state, sync, er, cb);else { - // Check if we're actually ready to finish, but don't emit yet - var finished = needFinish(state) || stream.destroyed; +AbstractLevelDOWN.prototype.getMany = function (keys, options, callback) { + callback = getCallback(options, callback) + callback = catering.fromCallback(callback) + options = getOptions(options) - if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) { - clearBuffer(stream, state); - } + if (maybeError(this, callback)) { + return callback.promise + } - if (sync) { - process.nextTick(afterWrite, stream, state, finished, cb); - } else { - afterWrite(stream, state, finished, cb); - } + if (!Array.isArray(keys)) { + this._nextTick(callback, new Error('getMany() requires an array argument')) + return callback.promise } -} -function afterWrite(stream, state, finished, cb) { - if (!finished) onwriteDrain(stream, state); - state.pendingcb--; - cb(); - finishMaybe(stream, state); -} // Must force callback to be called on nextTick, so that we don't -// emit 'drain' before the write() consumer gets the 'false' return -// value, and has a chance to attach a 'drain' listener. + if (keys.length === 0) { + this._nextTick(callback, null, []) + return callback.promise + } + + if (typeof options.asBuffer !== 'boolean') { + options = { ...options, asBuffer: true } + } + const serialized = new Array(keys.length) -function onwriteDrain(stream, state) { - if (state.length === 0 && state.needDrain) { - state.needDrain = false; - stream.emit('drain'); + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + const err = this._checkKey(key) + + if (err) { + this._nextTick(callback, err) + return callback.promise + } + + serialized[i] = this._serializeKey(key) } -} // if there's something in the buffer waiting, then process it + this._getMany(serialized, options, callback) + return callback.promise +} -function clearBuffer(stream, state) { - state.bufferProcessing = true; - var entry = state.bufferedRequest; +AbstractLevelDOWN.prototype._getMany = function (keys, options, callback) { + this._nextTick(callback, null, new Array(keys.length).fill(undefined)) +} - if (stream._writev && entry && entry.next) { - // Fast case, write everything using _writev() - var l = state.bufferedRequestCount; - var buffer = new Array(l); - var holder = state.corkedRequestsFree; - holder.entry = entry; - var count = 0; - var allBuffers = true; +AbstractLevelDOWN.prototype.put = function (key, value, options, callback) { + if (typeof options === 'function') callback = options - while (entry) { - buffer[count] = entry; - if (!entry.isBuf) allBuffers = false; - entry = entry.next; - count += 1; - } + if (typeof callback !== 'function') { + throw new Error('put() requires a callback argument') + } + + const err = this._checkKey(key) || this._checkValue(value) + if (err) return this._nextTick(callback, err) - buffer.allBuffers = allBuffers; - doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time - // as the hot path ends with doWrite + key = this._serializeKey(key) + value = this._serializeValue(value) - state.pendingcb++; - state.lastBufferedRequest = null; + if (typeof options !== 'object' || options === null) options = {} - if (holder.next) { - state.corkedRequestsFree = holder.next; - holder.next = null; - } else { - state.corkedRequestsFree = new CorkedRequest(state); - } + this._put(key, value, options, callback) +} - state.bufferedRequestCount = 0; - } else { - // Slow case, write chunks one-by-one - while (entry) { - var chunk = entry.chunk; - var encoding = entry.encoding; - var cb = entry.callback; - var len = state.objectMode ? 1 : chunk.length; - doWrite(stream, state, false, len, chunk, encoding, cb); - entry = entry.next; - state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then - // it means that we need to wait until it does. - // also, that means that the chunk and cb are currently - // being processed, so move the buffer counter past them. +AbstractLevelDOWN.prototype._put = function (key, value, options, callback) { + this._nextTick(callback) +} - if (state.writing) { - break; - } - } +AbstractLevelDOWN.prototype.del = function (key, options, callback) { + if (typeof options === 'function') callback = options - if (entry === null) state.lastBufferedRequest = null; + if (typeof callback !== 'function') { + throw new Error('del() requires a callback argument') } - state.bufferedRequest = entry; - state.bufferProcessing = false; -} + const err = this._checkKey(key) + if (err) return this._nextTick(callback, err) -Writable.prototype._write = function (chunk, encoding, cb) { - cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()')); -}; + key = this._serializeKey(key) -Writable.prototype._writev = null; + if (typeof options !== 'object' || options === null) options = {} -Writable.prototype.end = function (chunk, encoding, cb) { - var state = this._writableState; + this._del(key, options, callback) +} - if (typeof chunk === 'function') { - cb = chunk; - chunk = null; - encoding = null; - } else if (typeof encoding === 'function') { - cb = encoding; - encoding = null; - } +AbstractLevelDOWN.prototype._del = function (key, options, callback) { + this._nextTick(callback) +} - if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks +AbstractLevelDOWN.prototype.batch = function (array, options, callback) { + if (!arguments.length) return this._chainedBatch() - if (state.corked) { - state.corked = 1; - this.uncork(); - } // ignore unnecessary end() calls. + if (typeof options === 'function') callback = options + if (typeof array === 'function') callback = array - if (!state.ending) endWritable(this, state, cb); - return this; -}; + if (typeof callback !== 'function') { + throw new Error('batch(array) requires a callback argument') + } -Object.defineProperty(Writable.prototype, 'writableLength', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - return this._writableState.length; + if (!Array.isArray(array)) { + return this._nextTick(callback, new Error('batch(array) requires an array argument')) } -}); -function needFinish(state) { - return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing; -} + if (array.length === 0) { + return this._nextTick(callback) + } -function callFinal(stream, state) { - stream._final(function (err) { - state.pendingcb--; + if (typeof options !== 'object' || options === null) options = {} - if (err) { - errorOrDestroy(stream, err); + const serialized = new Array(array.length) + + for (let i = 0; i < array.length; i++) { + if (typeof array[i] !== 'object' || array[i] === null) { + return this._nextTick(callback, new Error('batch(array) element must be an object and not `null`')) } - state.prefinished = true; - stream.emit('prefinish'); - finishMaybe(stream, state); - }); -} + const e = Object.assign({}, array[i]) -function prefinish(stream, state) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === 'function' && !state.destroyed) { - state.pendingcb++; - state.finalCalled = true; - process.nextTick(callFinal, stream, state); - } else { - state.prefinished = true; - stream.emit('prefinish'); + if (e.type !== 'put' && e.type !== 'del') { + return this._nextTick(callback, new Error("`type` must be 'put' or 'del'")) } - } -} - -function finishMaybe(stream, state) { - var need = needFinish(state); - if (need) { - prefinish(stream, state); + const err = this._checkKey(e.key) + if (err) return this._nextTick(callback, err) - if (state.pendingcb === 0) { - state.finished = true; - stream.emit('finish'); + e.key = this._serializeKey(e.key) - if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the readable side is ready for autoDestroy as well - var rState = stream._readableState; + if (e.type === 'put') { + const valueErr = this._checkValue(e.value) + if (valueErr) return this._nextTick(callback, valueErr) - if (!rState || rState.autoDestroy && rState.endEmitted) { - stream.destroy(); - } - } + e.value = this._serializeValue(e.value) } + + serialized[i] = e } - return need; + this._batch(serialized, options, callback) } -function endWritable(stream, state, cb) { - state.ending = true; - finishMaybe(stream, state); +AbstractLevelDOWN.prototype._batch = function (array, options, callback) { + this._nextTick(callback) +} - if (cb) { - if (state.finished) process.nextTick(cb);else stream.once('finish', cb); +AbstractLevelDOWN.prototype.clear = function (options, callback) { + if (typeof options === 'function') { + callback = options + } else if (typeof callback !== 'function') { + throw new Error('clear() requires a callback argument') } - state.ended = true; - stream.writable = false; + options = cleanRangeOptions(this, options) + options.reverse = !!options.reverse + options.limit = 'limit' in options ? options.limit : -1 + + this._clear(options, callback) } -function onCorkedFinish(corkReq, state, err) { - var entry = corkReq.entry; - corkReq.entry = null; +AbstractLevelDOWN.prototype._clear = function (options, callback) { + // Avoid setupIteratorOptions, would serialize range options a second time. + options.keys = true + options.values = false + options.keyAsBuffer = true + options.valueAsBuffer = true - while (entry) { - var cb = entry.callback; - state.pendingcb--; - cb(err); - entry = entry.next; - } // reuse the free corkReq. + const iterator = this._iterator(options) + const emptyOptions = {} + const next = (err) => { + if (err) { + return iterator.end(function () { + callback(err) + }) + } - state.corkedRequestsFree.next = corkReq; + iterator.next((err, key) => { + if (err) return next(err) + if (key === undefined) return iterator.end(callback) + + // This could be optimized by using a batch, but the default _clear + // is not meant to be fast. Implementations have more room to optimize + // if they override _clear. Note: using _del bypasses key serialization. + this._del(key, emptyOptions, next) + }) + } + + next() } -Object.defineProperty(Writable.prototype, 'destroyed', { - // making it explicit this property is not enumerable - // because otherwise some prototype manipulation in - // userland will fail - enumerable: false, - get: function get() { - if (this._writableState === undefined) { - return false; +AbstractLevelDOWN.prototype._setupIteratorOptions = function (options) { + options = cleanRangeOptions(this, options) + + options.reverse = !!options.reverse + options.keys = options.keys !== false + options.values = options.values !== false + options.limit = 'limit' in options ? options.limit : -1 + options.keyAsBuffer = options.keyAsBuffer !== false + options.valueAsBuffer = options.valueAsBuffer !== false + + return options +} + +function cleanRangeOptions (db, options) { + const result = {} + + for (const k in options) { + if (!hasOwnProperty.call(options, k)) continue + + if (k === 'start' || k === 'end') { + throw new Error('Legacy range options ("start" and "end") have been removed') } - return this._writableState.destroyed; - }, - set: function set(value) { - // we ignore the value if the stream - // has not been initialized yet - if (!this._writableState) { - return; - } // backward compatibility, the user is explicitly - // managing destroyed + let opt = options[k] + if (isRangeOption(k)) { + // Note that we don't reject nullish and empty options here. While + // those types are invalid as keys, they are valid as range options. + opt = db._serializeKey(opt) + } - this._writableState.destroyed = value; + result[k] = opt } -}); -Writable.prototype.destroy = destroyImpl.destroy; -Writable.prototype._undestroy = destroyImpl.undestroy; - -Writable.prototype._destroy = function (err, cb) { - cb(err); -}; -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../errors":119,"./_stream_duplex":120,"./internal/streams/destroy":127,"./internal/streams/state":131,"./internal/streams/stream":132,"_process":377,"buffer":135,"inherits":243,"util-deprecate":401}],125:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; -var _Object$setPrototypeO; + return result +} -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function isRangeOption (k) { + return rangeOptions.indexOf(k) !== -1 +} -var finished = require('./end-of-stream'); +AbstractLevelDOWN.prototype.iterator = function (options) { + if (typeof options !== 'object' || options === null) options = {} + options = this._setupIteratorOptions(options) + return this._iterator(options) +} -var kLastResolve = Symbol('lastResolve'); -var kLastReject = Symbol('lastReject'); -var kError = Symbol('error'); -var kEnded = Symbol('ended'); -var kLastPromise = Symbol('lastPromise'); -var kHandlePromise = Symbol('handlePromise'); -var kStream = Symbol('stream'); +AbstractLevelDOWN.prototype._iterator = function (options) { + return new AbstractIterator(this) +} -function createIterResult(value, done) { - return { - value: value, - done: done - }; +AbstractLevelDOWN.prototype._chainedBatch = function () { + return new AbstractChainedBatch(this) } -function readAndResolve(iter) { - var resolve = iter[kLastResolve]; +AbstractLevelDOWN.prototype._serializeKey = function (key) { + return key +} - if (resolve !== null) { - var data = iter[kStream].read(); // we defer if data is null - // we can be expecting either 'end' or - // 'error' +AbstractLevelDOWN.prototype._serializeValue = function (value) { + return value +} - if (data !== null) { - iter[kLastPromise] = null; - iter[kLastResolve] = null; - iter[kLastReject] = null; - resolve(createIterResult(data, false)); - } +AbstractLevelDOWN.prototype._checkKey = function (key) { + if (key === null || key === undefined) { + return new Error('key cannot be `null` or `undefined`') + } else if (isBuffer(key) && key.length === 0) { // TODO: replace with typed array check + return new Error('key cannot be an empty Buffer') + } else if (key === '') { + return new Error('key cannot be an empty String') + } else if (Array.isArray(key) && key.length === 0) { + return new Error('key cannot be an empty Array') } } -function onReadable(iter) { - // we wait for the next tick, because it might - // emit an error with process.nextTick - process.nextTick(readAndResolve, iter); +AbstractLevelDOWN.prototype._checkValue = function (value) { + if (value === null || value === undefined) { + return new Error('value cannot be `null` or `undefined`') + } } -function wrapForNext(lastPromise, iter) { - return function (resolve, reject) { - lastPromise.then(function () { - if (iter[kEnded]) { - resolve(createIterResult(undefined, true)); - return; - } +// TODO: docs and tests +AbstractLevelDOWN.prototype.isOperational = function () { + return this.status === 'open' || this._isOperational() +} - iter[kHandlePromise](resolve, reject); - }, reject); - }; +// Implementation may accept operations in other states too +AbstractLevelDOWN.prototype._isOperational = function () { + return false } -var AsyncIteratorPrototype = Object.getPrototypeOf(function () {}); -var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = { - get stream() { - return this[kStream]; - }, +// Expose browser-compatible nextTick for dependents +// TODO: rename _nextTick to _queueMicrotask +// TODO: after we drop node 10, also use queueMicrotask in node +AbstractLevelDOWN.prototype._nextTick = require('./next-tick') - next: function next() { - var _this = this; +module.exports = AbstractLevelDOWN - // if we have detected an error in the meanwhile - // reject straight away - var error = this[kError]; +function maybeError (db, callback) { + if (!db.isOperational()) { + db._nextTick(callback, new Error('Database is not open')) + return true + } - if (error !== null) { - return Promise.reject(error); - } + return false +} - if (this[kEnded]) { - return Promise.resolve(createIterResult(undefined, true)); - } +},{"./abstract-chained-batch":77,"./abstract-iterator":78,"./lib/common":81,"./next-tick":82,"catering":104,"is-buffer":142,"level-supports":155}],80:[function(require,module,exports){ +'use strict' - if (this[kStream].destroyed) { - // We need to defer via nextTick because if .destroy(err) is - // called, the error will be emitted via nextTick, and - // we cannot guarantee that there is no error lingering around - // waiting to be emitted. - return new Promise(function (resolve, reject) { - process.nextTick(function () { - if (_this[kError]) { - reject(_this[kError]); - } else { - resolve(createIterResult(undefined, true)); - } - }); - }); - } // if we have multiple next() calls - // we will wait for the previous Promise to finish - // this logic is optimized to support for await loops, - // where next() is only called once at a time +exports.AbstractLevelDOWN = require('./abstract-leveldown') +exports.AbstractIterator = require('./abstract-iterator') +exports.AbstractChainedBatch = require('./abstract-chained-batch') +},{"./abstract-chained-batch":77,"./abstract-iterator":78,"./abstract-leveldown":79}],81:[function(require,module,exports){ +'use strict' - var lastPromise = this[kLastPromise]; - var promise; +exports.getCallback = function (options, callback) { + return typeof options === 'function' ? options : callback +} - if (lastPromise) { - promise = new Promise(wrapForNext(lastPromise, this)); - } else { - // fast path needed to support multiple this.push() - // without triggering the next() queue - var data = this[kStream].read(); +exports.getOptions = function (options) { + return typeof options === 'object' && options !== null ? options : {} +} - if (data !== null) { - return Promise.resolve(createIterResult(data, false)); - } +},{}],82:[function(require,module,exports){ +'use strict' - promise = new Promise(this[kHandlePromise]); - } +const queueMicrotask = require('queue-microtask') - this[kLastPromise] = promise; - return promise; +module.exports = function (fn, ...args) { + if (args.length === 0) { + queueMicrotask(fn) + } else { + queueMicrotask(() => fn(...args)) } -}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () { - return this; -}), _defineProperty(_Object$setPrototypeO, "return", function _return() { - var _this2 = this; - - // destroy(err, cb) is a private API - // we can guarantee we have that here, because we control the - // Readable class this is attached to - return new Promise(function (resolve, reject) { - _this2[kStream].destroy(null, function (err) { - if (err) { - reject(err); - return; - } +} - resolve(createIterResult(undefined, true)); - }); - }); -}), _Object$setPrototypeO), AsyncIteratorPrototype); +},{"queue-microtask":181}],83:[function(require,module,exports){ +const { encodeChange } = require('./columnar') +const { BackendDoc } = require('./new') +const { backendState } = require('./util') -var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) { - var _Object$create; +/** + * Returns an empty node state. + */ +function init() { + return {state: new BackendDoc(), heads: []} +} - var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, { - value: stream, - writable: true - }), _defineProperty(_Object$create, kLastResolve, { - value: null, - writable: true - }), _defineProperty(_Object$create, kLastReject, { - value: null, - writable: true - }), _defineProperty(_Object$create, kError, { - value: null, - writable: true - }), _defineProperty(_Object$create, kEnded, { - value: stream._readableState.endEmitted, - writable: true - }), _defineProperty(_Object$create, kHandlePromise, { - value: function value(resolve, reject) { - var data = iterator[kStream].read(); +function clone(backend) { + return {state: backendState(backend).clone(), heads: backend.heads} +} - if (data) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(data, false)); - } else { - iterator[kLastResolve] = resolve; - iterator[kLastReject] = reject; - } - }, - writable: true - }), _Object$create)); - iterator[kLastPromise] = null; - finished(stream, function (err) { - if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') { - var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise - // returned by next() and store the error +function free(backend) { + backend.state = null + backend.frozen = true +} - if (reject !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - reject(err); - } +/** + * Applies a list of `changes` from remote nodes to the node state `backend`. + * Returns a two-element array `[state, patch]` where `state` is the updated + * node state, and `patch` describes the modifications that need to be made + * to the document objects to reflect these changes. + */ +function applyChanges(backend, changes) { + const state = backendState(backend) + const patch = state.applyChanges(changes) + backend.frozen = true + return [{state, heads: state.heads}, patch] +} - iterator[kError] = err; - return; +function hashByActor(state, actorId, index) { + if (state.hashesByActor[actorId] && state.hashesByActor[actorId][index]) { + return state.hashesByActor[actorId][index] + } + if (!state.haveHashGraph) { + state.computeHashGraph() + if (state.hashesByActor[actorId] && state.hashesByActor[actorId][index]) { + return state.hashesByActor[actorId][index] } + } + throw new RangeError(`Unknown change: actorId = ${actorId}, seq = ${index + 1}`) +} - var resolve = iterator[kLastResolve]; +/** + * Takes a single change request `request` made by the local user, and applies + * it to the node state `backend`. Returns a three-element array `[backend, patch, binaryChange]` + * where `backend` is the updated node state,`patch` confirms the + * modifications to the document objects, and `binaryChange` is a binary-encoded form of + * the change submitted. + */ +function applyLocalChange(backend, change) { + const state = backendState(backend) + if (change.seq <= state.clock[change.actor] || 0) { + throw new RangeError('Change request has already been applied') + } - if (resolve !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(undefined, true)); + // Add the local actor's last change hash to deps. We do this because when frontend + // and backend are on separate threads, the frontend may fire off several local + // changes in sequence before getting a response from the backend; since the binary + // encoding and hashing is done by the backend, the frontend does not know the hash + // of its own last change in this case. Rather than handle this situation as a + // special case, we say that the frontend includes only specifies other actors' + // deps in changes it generates, and the dependency from the local actor's last + // change is always added here in the backend. + // + // Strictly speaking, we should check whether the local actor's last change is + // indirectly reachable through a different actor's change; in that case, it is not + // necessary to add this dependency. However, it doesn't do any harm either (only + // using a few extra bytes of storage). + if (change.seq > 1) { + const lastHash = hashByActor(state, change.actor, change.seq - 2) + if (!lastHash) { + throw new RangeError(`Cannot find hash of localChange before seq=${change.seq}`) } + let deps = {[lastHash]: true} + for (let hash of change.deps) deps[hash] = true + change.deps = Object.keys(deps).sort() + } - iterator[kEnded] = true; - }); - stream.on('readable', onReadable.bind(null, iterator)); - return iterator; -}; + const binaryChange = encodeChange(change) + const patch = state.applyChanges([binaryChange], true) + backend.frozen = true -module.exports = createReadableStreamAsyncIterator; -}).call(this)}).call(this,require('_process')) -},{"./end-of-stream":128,"_process":377}],126:[function(require,module,exports){ -'use strict'; + // On the patch we send out, omit the last local change hash + const lastHash = hashByActor(state, change.actor, change.seq - 1) + patch.deps = patch.deps.filter(head => head !== lastHash) + return [{state, heads: state.heads}, patch, binaryChange] +} -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } +/** + * Returns the state of the document serialised to an Uint8Array. + */ +function save(backend) { + return backendState(backend).save() +} -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } +/** + * Loads the document and/or changes contained in an Uint8Array, and returns a + * backend initialised with this state. + */ +function load(data) { + const state = new BackendDoc(data) + return {state, heads: state.heads} +} -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +/** + * Applies a list of `changes` to the node state `backend`, and returns the updated + * state with those changes incorporated. Unlike `applyChanges()`, this function + * does not produce a patch describing the incremental modifications, making it + * a little faster when loading a document from disk. When all the changes have + * been loaded, you can use `getPatch()` to construct the latest document state. + */ +function loadChanges(backend, changes) { + const state = backendState(backend) + state.applyChanges(changes) + backend.frozen = true + return {state, heads: state.heads} +} -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +/** + * Returns a patch that, when applied to an empty document, constructs the + * document tree in the state described by the node state `backend`. + */ +function getPatch(backend) { + return backendState(backend).getPatch() +} -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); } } +/** + * Returns an array of hashes of the current "head" changes (i.e. those changes + * that no other change depends on). + */ +function getHeads(backend) { + return backend.heads +} -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } +/** + * Returns the full history of changes that have been applied to a document. + */ +function getAllChanges(backend) { + return getChanges(backend, []) +} -var _require = require('buffer'), - Buffer = _require.Buffer; +/** + * Returns all changes that are newer than or concurrent to the changes + * identified by the hashes in `haveDeps`. If `haveDeps` is an empty array, all + * changes are returned. Throws an exception if any of the given hashes is unknown. + */ +function getChanges(backend, haveDeps) { + if (!Array.isArray(haveDeps)) { + throw new TypeError('Pass an array of hashes to Backend.getChanges()') + } + return backendState(backend).getChanges(haveDeps) +} -var _require2 = require('util'), - inspect = _require2.inspect; +/** + * Returns all changes that are present in `backend2` but not in `backend1`. + * Intended for use in situations where the two backends are for different actors. + * To get the changes added between an older and a newer document state of the same + * actor, use `getChanges()` instead. `getChangesAdded()` throws an exception if + * one of the backend states is frozen (i.e. if it is not the latest state of that + * backend instance; this distinction matters when the backend is mutable). + */ +function getChangesAdded(backend1, backend2) { + return backendState(backend2).getChangesAdded(backendState(backend1)) +} -var custom = inspect && inspect.custom || 'inspect'; +/** + * If the backend has applied a change with the given `hash` (given as a + * hexadecimal string), returns that change (as a byte array). Returns undefined + * if no change with that hash has been applied. A change with missing + * dependencies does not count as having been applied. + */ +function getChangeByHash(backend, hash) { + return backendState(backend).getChangeByHash(hash) +} -function copyBuffer(src, target, offset) { - Buffer.prototype.copy.call(src, target, offset); +/** + * Returns the hashes of any missing dependencies, i.e. where we have applied a + * change that has a dependency on a change we have not seen. + * + * If the argument `heads` is given (an array of hexadecimal strings representing + * hashes as returned by `getHeads()`), this function also ensures that all of + * those hashes resolve to either a change that has been applied to the document, + * or that has been enqueued for later application once missing dependencies have + * arrived. Any missing heads hashes are included in the returned array. + */ +function getMissingDeps(backend, heads = []) { + return backendState(backend).getMissingDeps(heads) } -module.exports = -/*#__PURE__*/ -function () { - function BufferList() { - _classCallCheck(this, BufferList); +module.exports = { + init, clone, free, applyChanges, applyLocalChange, save, load, loadChanges, getPatch, + getHeads, getAllChanges, getChanges, getChangesAdded, getChangeByHash, getMissingDeps +} - this.head = null; - this.tail = null; - this.length = 0; - } +},{"./columnar":84,"./new":87,"./util":89}],84:[function(require,module,exports){ +const pako = require('pako') +const { copyObject, parseOpId, equalBytes } = require('../src/common') +const { + utf8ToString, hexStringToBytes, bytesToHexString, + Encoder, Decoder, RLEEncoder, RLEDecoder, DeltaEncoder, DeltaDecoder, BooleanEncoder, BooleanDecoder +} = require('./encoding') - _createClass(BufferList, [{ - key: "push", - value: function push(v) { - var entry = { - data: v, - next: null - }; - if (this.length > 0) this.tail.next = entry;else this.head = entry; - this.tail = entry; - ++this.length; - } - }, { - key: "unshift", - value: function unshift(v) { - var entry = { - data: v, - next: this.head - }; - if (this.length === 0) this.tail = entry; - this.head = entry; - ++this.length; - } - }, { - key: "shift", - value: function shift() { - if (this.length === 0) return; - var ret = this.head.data; - if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next; - --this.length; - return ret; - } - }, { - key: "clear", - value: function clear() { - this.head = this.tail = null; - this.length = 0; - } - }, { - key: "join", - value: function join(s) { - if (this.length === 0) return ''; - var p = this.head; - var ret = '' + p.data; +// Maybe we should be using the platform's built-in hash implementation? +// Node has the crypto module: https://nodejs.org/api/crypto.html and browsers have +// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest +// However, the WebCrypto API is asynchronous (returns promises), which would +// force all our APIs to become asynchronous as well, which would be annoying. +// +// I think on balance, it's safe enough to use a random library off npm: +// - We only need one hash function (not a full suite of crypto algorithms); +// - SHA256 is quite simple and has fairly few opportunities for subtle bugs +// (compared to asymmetric cryptography anyway); +// - It does not need a secure source of random bits and does not need to be +// constant-time; +// - I have reviewed the source code and it seems pretty reasonable. +const { Hash } = require('fast-sha256') - while (p = p.next) { - ret += s + p.data; - } +// These bytes don't mean anything, they were generated randomly +const MAGIC_BYTES = new Uint8Array([0x85, 0x6f, 0x4a, 0x83]) - return ret; - } - }, { - key: "concat", - value: function concat(n) { - if (this.length === 0) return Buffer.alloc(0); - var ret = Buffer.allocUnsafe(n >>> 0); - var p = this.head; - var i = 0; +const CHUNK_TYPE_DOCUMENT = 0 +const CHUNK_TYPE_CHANGE = 1 +const CHUNK_TYPE_DEFLATE = 2 // like CHUNK_TYPE_CHANGE but with DEFLATE compression - while (p) { - copyBuffer(p.data, ret, i); - i += p.data.length; - p = p.next; - } +// Minimum number of bytes in a value before we enable DEFLATE compression (there is no point +// compressing very short values since compression may actually make them bigger) +const DEFLATE_MIN_SIZE = 256 - return ret; - } // Consumes a specified amount of bytes or characters from the buffered data. +// The least-significant 3 bits of a columnId indicate its datatype +const COLUMN_TYPE = { + GROUP_CARD: 0, ACTOR_ID: 1, INT_RLE: 2, INT_DELTA: 3, BOOLEAN: 4, + STRING_RLE: 5, VALUE_LEN: 6, VALUE_RAW: 7 +} - }, { - key: "consume", - value: function consume(n, hasStrings) { - var ret; +// The 4th-least-significant bit of a columnId is set if the column is DEFLATE-compressed +const COLUMN_TYPE_DEFLATE = 8 - if (n < this.head.data.length) { - // `slice` is the same for buffers and strings. - ret = this.head.data.slice(0, n); - this.head.data = this.head.data.slice(n); - } else if (n === this.head.data.length) { - // First chunk is a perfect match. - ret = this.shift(); - } else { - // Result spans more than one buffer. - ret = hasStrings ? this._getString(n) : this._getBuffer(n); - } +// In the values in a column of type VALUE_LEN, the bottom four bits indicate the type of the value, +// one of the following types in VALUE_TYPE. The higher bits indicate the length of the value in the +// associated VALUE_RAW column (in bytes). +const VALUE_TYPE = { + NULL: 0, FALSE: 1, TRUE: 2, LEB128_UINT: 3, LEB128_INT: 4, IEEE754: 5, + UTF8: 6, BYTES: 7, COUNTER: 8, TIMESTAMP: 9, MIN_UNKNOWN: 10, MAX_UNKNOWN: 15 +} - return ret; - } - }, { - key: "first", - value: function first() { - return this.head.data; - } // Consumes a specified amount of characters from the buffered data. +// make* actions must be at even-numbered indexes in this list +const ACTIONS = ['makeMap', 'set', 'makeList', 'del', 'makeText', 'inc', 'makeTable', 'link'] - }, { - key: "_getString", - value: function _getString(n) { - var p = this.head; - var c = 1; - var ret = p.data; - n -= ret.length; +const OBJECT_TYPE = {makeMap: 'map', makeList: 'list', makeText: 'text', makeTable: 'table'} - while (p = p.next) { - var str = p.data; - var nb = n > str.length ? str.length : n; - if (nb === str.length) ret += str;else ret += str.slice(0, n); - n -= nb; +const COMMON_COLUMNS = [ + {columnName: 'objActor', columnId: 0 << 4 | COLUMN_TYPE.ACTOR_ID}, + {columnName: 'objCtr', columnId: 0 << 4 | COLUMN_TYPE.INT_RLE}, + {columnName: 'keyActor', columnId: 1 << 4 | COLUMN_TYPE.ACTOR_ID}, + {columnName: 'keyCtr', columnId: 1 << 4 | COLUMN_TYPE.INT_DELTA}, + {columnName: 'keyStr', columnId: 1 << 4 | COLUMN_TYPE.STRING_RLE}, + {columnName: 'idActor', columnId: 2 << 4 | COLUMN_TYPE.ACTOR_ID}, + {columnName: 'idCtr', columnId: 2 << 4 | COLUMN_TYPE.INT_DELTA}, + {columnName: 'insert', columnId: 3 << 4 | COLUMN_TYPE.BOOLEAN}, + {columnName: 'action', columnId: 4 << 4 | COLUMN_TYPE.INT_RLE}, + {columnName: 'valLen', columnId: 5 << 4 | COLUMN_TYPE.VALUE_LEN}, + {columnName: 'valRaw', columnId: 5 << 4 | COLUMN_TYPE.VALUE_RAW}, + {columnName: 'chldActor', columnId: 6 << 4 | COLUMN_TYPE.ACTOR_ID}, + {columnName: 'chldCtr', columnId: 6 << 4 | COLUMN_TYPE.INT_DELTA} +] - if (n === 0) { - if (nb === str.length) { - ++c; - if (p.next) this.head = p.next;else this.head = this.tail = null; - } else { - this.head = p; - p.data = str.slice(nb); - } +const CHANGE_COLUMNS = COMMON_COLUMNS.concat([ + {columnName: 'predNum', columnId: 7 << 4 | COLUMN_TYPE.GROUP_CARD}, + {columnName: 'predActor', columnId: 7 << 4 | COLUMN_TYPE.ACTOR_ID}, + {columnName: 'predCtr', columnId: 7 << 4 | COLUMN_TYPE.INT_DELTA} +]) - break; - } +const DOC_OPS_COLUMNS = COMMON_COLUMNS.concat([ + {columnName: 'succNum', columnId: 8 << 4 | COLUMN_TYPE.GROUP_CARD}, + {columnName: 'succActor', columnId: 8 << 4 | COLUMN_TYPE.ACTOR_ID}, + {columnName: 'succCtr', columnId: 8 << 4 | COLUMN_TYPE.INT_DELTA} +]) - ++c; - } +const DOCUMENT_COLUMNS = [ + {columnName: 'actor', columnId: 0 << 4 | COLUMN_TYPE.ACTOR_ID}, + {columnName: 'seq', columnId: 0 << 4 | COLUMN_TYPE.INT_DELTA}, + {columnName: 'maxOp', columnId: 1 << 4 | COLUMN_TYPE.INT_DELTA}, + {columnName: 'time', columnId: 2 << 4 | COLUMN_TYPE.INT_DELTA}, + {columnName: 'message', columnId: 3 << 4 | COLUMN_TYPE.STRING_RLE}, + {columnName: 'depsNum', columnId: 4 << 4 | COLUMN_TYPE.GROUP_CARD}, + {columnName: 'depsIndex', columnId: 4 << 4 | COLUMN_TYPE.INT_DELTA}, + {columnName: 'extraLen', columnId: 5 << 4 | COLUMN_TYPE.VALUE_LEN}, + {columnName: 'extraRaw', columnId: 5 << 4 | COLUMN_TYPE.VALUE_RAW} +] - this.length -= c; - return ret; - } // Consumes a specified amount of bytes from the buffered data. +/** + * Maps an opId of the form {counter: 12345, actorId: 'someActorId'} to the form + * {counter: 12345, actorNum: 123, actorId: 'someActorId'}, where the actorNum + * is the index into the `actorIds` array. + */ +function actorIdToActorNum(opId, actorIds) { + if (!opId || !opId.actorId) return opId + const counter = opId.counter + const actorNum = actorIds.indexOf(opId.actorId) + if (actorNum < 0) throw new RangeError('missing actorId') // should not happen + return {counter, actorNum, actorId: opId.actorId} +} - }, { - key: "_getBuffer", - value: function _getBuffer(n) { - var ret = Buffer.allocUnsafe(n); - var p = this.head; - var c = 1; - p.data.copy(ret); - n -= p.data.length; +/** + * Comparison function to pass to Array.sort(), which compares two opIds in the + * form produced by `actorIdToActorNum` so that they are sorted in increasing + * Lamport timestamp order (sorted first by counter, then by actorId). + */ +function compareParsedOpIds(id1, id2) { + if (id1.counter < id2.counter) return -1 + if (id1.counter > id2.counter) return +1 + if (id1.actorId < id2.actorId) return -1 + if (id1.actorId > id2.actorId) return +1 + return 0 +} - while (p = p.next) { - var buf = p.data; - var nb = n > buf.length ? buf.length : n; - buf.copy(ret, ret.length - n, 0, nb); - n -= nb; +/** + * Takes `changes`, an array of changes (represented as JS objects). Returns an + * object `{changes, actorIds}`, where `changes` is a copy of the argument in + * which all string opIds have been replaced with `{counter, actorNum}` objects, + * and where `actorIds` is a lexicographically sorted array of actor IDs occurring + * in any of the operations. `actorNum` is an index into that array of actorIds. + * If `single` is true, the actorId of the author of the change is moved to the + * beginning of the array of actorIds, so that `actorNum` is zero when referencing + * the author of the change itself. This special-casing is omitted if `single` is + * false. + */ +function parseAllOpIds(changes, single) { + const actors = {}, newChanges = [] + for (let change of changes) { + change = copyObject(change) + actors[change.actor] = true + change.ops = expandMultiOps(change.ops, change.startOp, change.actor) + change.ops = change.ops.map(op => { + op = copyObject(op) + if (op.obj !== '_root') op.obj = parseOpId(op.obj) + if (op.elemId && op.elemId !== '_head') op.elemId = parseOpId(op.elemId) + if (op.child) op.child = parseOpId(op.child) + if (op.pred) op.pred = op.pred.map(parseOpId) + if (op.obj.actorId) actors[op.obj.actorId] = true + if (op.elemId && op.elemId.actorId) actors[op.elemId.actorId] = true + if (op.child && op.child.actorId) actors[op.child.actorId] = true + for (let pred of op.pred) actors[pred.actorId] = true + return op + }) + newChanges.push(change) + } - if (n === 0) { - if (nb === buf.length) { - ++c; - if (p.next) this.head = p.next;else this.head = this.tail = null; - } else { - this.head = p; - p.data = buf.slice(nb); - } + let actorIds = Object.keys(actors).sort() + if (single) { + actorIds = [changes[0].actor].concat(actorIds.filter(actor => actor !== changes[0].actor)) + } + for (let change of newChanges) { + change.actorNum = actorIds.indexOf(change.actor) + for (let i = 0; i < change.ops.length; i++) { + let op = change.ops[i] + op.id = {counter: change.startOp + i, actorNum: change.actorNum, actorId: change.actor} + op.obj = actorIdToActorNum(op.obj, actorIds) + op.elemId = actorIdToActorNum(op.elemId, actorIds) + op.child = actorIdToActorNum(op.child, actorIds) + op.pred = op.pred.map(pred => actorIdToActorNum(pred, actorIds)) + } + } + return {changes: newChanges, actorIds} +} + +/** + * Encodes the `obj` property of operation `op` into the two columns + * `objActor` and `objCtr`. + */ +function encodeObjectId(op, columns) { + if (op.obj === '_root') { + columns.objActor.appendValue(null) + columns.objCtr.appendValue(null) + } else if (op.obj.actorNum >= 0 && op.obj.counter > 0) { + columns.objActor.appendValue(op.obj.actorNum) + columns.objCtr.appendValue(op.obj.counter) + } else { + throw new RangeError(`Unexpected objectId reference: ${JSON.stringify(op.obj)}`) + } +} + +/** + * Encodes the `key` and `elemId` properties of operation `op` into the three + * columns `keyActor`, `keyCtr`, and `keyStr`. + */ +function encodeOperationKey(op, columns) { + if (op.key) { + columns.keyActor.appendValue(null) + columns.keyCtr.appendValue(null) + columns.keyStr.appendValue(op.key) + } else if (op.elemId === '_head' && op.insert) { + columns.keyActor.appendValue(null) + columns.keyCtr.appendValue(0) + columns.keyStr.appendValue(null) + } else if (op.elemId && op.elemId.actorNum >= 0 && op.elemId.counter > 0) { + columns.keyActor.appendValue(op.elemId.actorNum) + columns.keyCtr.appendValue(op.elemId.counter) + columns.keyStr.appendValue(null) + } else { + throw new RangeError(`Unexpected operation key: ${JSON.stringify(op)}`) + } +} - break; - } +/** + * Encodes the `action` property of operation `op` into the `action` column. + */ +function encodeOperationAction(op, columns) { + const actionCode = ACTIONS.indexOf(op.action) + if (actionCode >= 0) { + columns.action.appendValue(actionCode) + } else if (typeof op.action === 'number') { + columns.action.appendValue(op.action) + } else { + throw new RangeError(`Unexpected operation action: ${op.action}`) + } +} - ++c; +/** + * Given the datatype for a number, determine the typeTag and the value to encode + * otherwise guess + */ +function getNumberTypeAndValue(op) { + switch (op.datatype) { + case "counter": + return [ VALUE_TYPE.COUNTER, op.value ] + case "timestamp": + return [ VALUE_TYPE.TIMESTAMP, op.value ] + case "uint": + return [ VALUE_TYPE.LEB128_UINT, op.value ] + case "int": + return [ VALUE_TYPE.LEB128_INT, op.value ] + case "float64": { + const buf64 = new ArrayBuffer(8), view64 = new DataView(buf64) + view64.setFloat64(0, op.value, true) + return [ VALUE_TYPE.IEEE754, new Uint8Array(buf64) ] + } + default: + // increment operators get resolved here ... + if (Number.isInteger(op.value) && op.value <= Number.MAX_SAFE_INTEGER && op.value >= Number.MIN_SAFE_INTEGER) { + return [ VALUE_TYPE.LEB128_INT, op.value ] + } else { + const buf64 = new ArrayBuffer(8), view64 = new DataView(buf64) + view64.setFloat64(0, op.value, true) + return [ VALUE_TYPE.IEEE754, new Uint8Array(buf64) ] } + } +} - this.length -= c; - return ret; - } // Make sure the linked list only shows the minimal necessary information. - - }, { - key: custom, - value: function value(_, options) { - return inspect(this, _objectSpread({}, options, { - // Only inspect one level. - depth: 0, - // It should not recurse. - customInspect: false - })); +/** + * Encodes the `value` property of operation `op` into the two columns + * `valLen` and `valRaw`. + */ +function encodeValue(op, columns) { + if ((op.action !== 'set' && op.action !== 'inc') || op.value === null) { + columns.valLen.appendValue(VALUE_TYPE.NULL) + } else if (op.value === false) { + columns.valLen.appendValue(VALUE_TYPE.FALSE) + } else if (op.value === true) { + columns.valLen.appendValue(VALUE_TYPE.TRUE) + } else if (typeof op.value === 'string') { + const numBytes = columns.valRaw.appendRawString(op.value) + columns.valLen.appendValue(numBytes << 4 | VALUE_TYPE.UTF8) + } else if (ArrayBuffer.isView(op.value)) { + const numBytes = columns.valRaw.appendRawBytes(new Uint8Array(op.value.buffer)) + columns.valLen.appendValue(numBytes << 4 | VALUE_TYPE.BYTES) + } else if (typeof op.value === 'number') { + let [typeTag, value] = getNumberTypeAndValue(op) + let numBytes + if (typeTag === VALUE_TYPE.LEB128_UINT) { + numBytes = columns.valRaw.appendUint53(value) + } else if (typeTag === VALUE_TYPE.IEEE754) { + numBytes = columns.valRaw.appendRawBytes(value) + } else { + numBytes = columns.valRaw.appendInt53(value) } - }]); - - return BufferList; -}(); -},{"buffer":135,"util":91}],127:[function(require,module,exports){ -(function (process){(function (){ -'use strict'; // undocumented cb() API, needed for core, not for public API - -function destroy(err, cb) { - var _this = this; - - var readableDestroyed = this._readableState && this._readableState.destroyed; - var writableDestroyed = this._writableState && this._writableState.destroyed; + columns.valLen.appendValue(numBytes << 4 | typeTag) + } else if (typeof op.datatype === 'number' && op.datatype >= VALUE_TYPE.MIN_UNKNOWN && + op.datatype <= VALUE_TYPE.MAX_UNKNOWN && op.value instanceof Uint8Array) { + const numBytes = columns.valRaw.appendRawBytes(op.value) + columns.valLen.appendValue(numBytes << 4 | op.datatype) + } else if (op.datatype) { + throw new RangeError(`Unknown datatype ${op.datatype} for value ${op.value}`) + } else { + throw new RangeError(`Unsupported value in operation: ${op.value}`) + } +} - if (readableDestroyed || writableDestroyed) { - if (cb) { - cb(err); - } else if (err) { - if (!this._writableState) { - process.nextTick(emitErrorNT, this, err); - } else if (!this._writableState.errorEmitted) { - this._writableState.errorEmitted = true; - process.nextTick(emitErrorNT, this, err); +/** + * Given `sizeTag` (an unsigned integer read from a VALUE_LEN column) and `bytes` (a Uint8Array + * read from a VALUE_RAW column, with length `sizeTag >> 4`), this function returns an object of the + * form `{value: value, datatype: datatypeTag}` where `value` is a JavaScript primitive datatype + * corresponding to the value, and `datatypeTag` is a datatype annotation such as 'counter'. + */ +function decodeValue(sizeTag, bytes) { + if (sizeTag === VALUE_TYPE.NULL) { + return {value: null} + } else if (sizeTag === VALUE_TYPE.FALSE) { + return {value: false} + } else if (sizeTag === VALUE_TYPE.TRUE) { + return {value: true} + } else if (sizeTag % 16 === VALUE_TYPE.UTF8) { + return {value: utf8ToString(bytes)} + } else { + if (sizeTag % 16 === VALUE_TYPE.LEB128_UINT) { + return {value: new Decoder(bytes).readUint53(), datatype: "uint"} + } else if (sizeTag % 16 === VALUE_TYPE.LEB128_INT) { + return {value: new Decoder(bytes).readInt53(), datatype: "int"} + } else if (sizeTag % 16 === VALUE_TYPE.IEEE754) { + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength) + if (bytes.byteLength === 8) { + return {value: view.getFloat64(0, true), datatype: "float64"} + } else { + throw new RangeError(`Invalid length for floating point number: ${bytes.byteLength}`) } + } else if (sizeTag % 16 === VALUE_TYPE.COUNTER) { + return {value: new Decoder(bytes).readInt53(), datatype: 'counter'} + } else if (sizeTag % 16 === VALUE_TYPE.TIMESTAMP) { + return {value: new Decoder(bytes).readInt53(), datatype: 'timestamp'} + } else { + return {value: bytes, datatype: sizeTag % 16} } + } +} - return this; - } // we set destroyed to true before firing error callbacks in order - // to make it re-entrance safe in case destroy() is called within callbacks +/** + * Reads one value from the column `columns[colIndex]` and interprets it based + * on the column type. `actorIds` is a list of actors that appear in the change; + * `actorIds[0]` is the actorId of the change's author. Mutates the `result` + * object with the value, and returns the number of columns processed (this is 2 + * in the case of a pair of VALUE_LEN and VALUE_RAW columns, which are processed + * in one go). + */ +function decodeValueColumns(columns, colIndex, actorIds, result) { + const { columnId, columnName, decoder } = columns[colIndex] + if (columnId % 8 === COLUMN_TYPE.VALUE_LEN && colIndex + 1 < columns.length && + columns[colIndex + 1].columnId === columnId + 1) { + const sizeTag = decoder.readValue() + const rawValue = columns[colIndex + 1].decoder.readRawBytes(sizeTag >> 4) + const { value, datatype } = decodeValue(sizeTag, rawValue) + result[columnName] = value + if (datatype) result[columnName + '_datatype'] = datatype + return 2 + } else if (columnId % 8 === COLUMN_TYPE.ACTOR_ID) { + const actorNum = decoder.readValue() + if (actorNum === null) { + result[columnName] = null + } else { + if (!actorIds[actorNum]) throw new RangeError(`No actor index ${actorNum}`) + result[columnName] = actorIds[actorNum] + } + } else { + result[columnName] = decoder.readValue() + } + return 1 +} +/** + * Encodes an array of operations in a set of columns. The operations need to + * be parsed with `parseAllOpIds()` beforehand. If `forDocument` is true, we use + * the column structure of a whole document, otherwise we use the column + * structure for an individual change. Returns an array of + * `{columnId, columnName, encoder}` objects. + */ +function encodeOps(ops, forDocument) { + const columns = { + objActor : new RLEEncoder('uint'), + objCtr : new RLEEncoder('uint'), + keyActor : new RLEEncoder('uint'), + keyCtr : new DeltaEncoder(), + keyStr : new RLEEncoder('utf8'), + insert : new BooleanEncoder(), + action : new RLEEncoder('uint'), + valLen : new RLEEncoder('uint'), + valRaw : new Encoder(), + chldActor : new RLEEncoder('uint'), + chldCtr : new DeltaEncoder() + } - if (this._readableState) { - this._readableState.destroyed = true; - } // if this is a duplex stream mark the writable part as destroyed as well + if (forDocument) { + columns.idActor = new RLEEncoder('uint') + columns.idCtr = new DeltaEncoder() + columns.succNum = new RLEEncoder('uint') + columns.succActor = new RLEEncoder('uint') + columns.succCtr = new DeltaEncoder() + } else { + columns.predNum = new RLEEncoder('uint') + columns.predCtr = new DeltaEncoder() + columns.predActor = new RLEEncoder('uint') + } + for (let op of ops) { + encodeObjectId(op, columns) + encodeOperationKey(op, columns) + columns.insert.appendValue(!!op.insert) + encodeOperationAction(op, columns) + encodeValue(op, columns) - if (this._writableState) { - this._writableState.destroyed = true; - } + if (op.child && op.child.counter) { + columns.chldActor.appendValue(op.child.actorNum) + columns.chldCtr.appendValue(op.child.counter) + } else { + columns.chldActor.appendValue(null) + columns.chldCtr.appendValue(null) + } - this._destroy(err || null, function (err) { - if (!cb && err) { - if (!_this._writableState) { - process.nextTick(emitErrorAndCloseNT, _this, err); - } else if (!_this._writableState.errorEmitted) { - _this._writableState.errorEmitted = true; - process.nextTick(emitErrorAndCloseNT, _this, err); - } else { - process.nextTick(emitCloseNT, _this); + if (forDocument) { + columns.idActor.appendValue(op.id.actorNum) + columns.idCtr.appendValue(op.id.counter) + columns.succNum.appendValue(op.succ.length) + op.succ.sort(compareParsedOpIds) + for (let i = 0; i < op.succ.length; i++) { + columns.succActor.appendValue(op.succ[i].actorNum) + columns.succCtr.appendValue(op.succ[i].counter) } - } else if (cb) { - process.nextTick(emitCloseNT, _this); - cb(err); } else { - process.nextTick(emitCloseNT, _this); + columns.predNum.appendValue(op.pred.length) + op.pred.sort(compareParsedOpIds) + for (let i = 0; i < op.pred.length; i++) { + columns.predActor.appendValue(op.pred[i].actorNum) + columns.predCtr.appendValue(op.pred[i].counter) + } } - }); + } - return this; + let columnList = [] + for (let {columnName, columnId} of forDocument ? DOC_OPS_COLUMNS : CHANGE_COLUMNS) { + if (columns[columnName]) columnList.push({columnId, columnName, encoder: columns[columnName]}) + } + return columnList.sort((a, b) => a.columnId - b.columnId) } -function emitErrorAndCloseNT(self, err) { - emitErrorNT(self, err); - emitCloseNT(self); +function validDatatype(value, datatype) { + if (datatype === undefined) { + return (typeof value === 'string' || typeof value === 'boolean' || value === null) + } else { + return typeof value === 'number' + } } -function emitCloseNT(self) { - if (self._writableState && !self._writableState.emitClose) return; - if (self._readableState && !self._readableState.emitClose) return; - self.emit('close'); +function expandMultiOps(ops, startOp, actor) { + let opNum = startOp + let expandedOps = [] + for (const op of ops) { + if (op.action === 'set' && op.values && op.insert) { + if (op.pred.length !== 0) throw new RangeError('multi-insert pred must be empty') + let lastElemId = op.elemId + const datatype = op.datatype + for (const value of op.values) { + if (!validDatatype(value, datatype)) throw new RangeError(`Decode failed: bad value/datatype association (${value},${datatype})`) + expandedOps.push({action: 'set', obj: op.obj, elemId: lastElemId, datatype, value, pred: [], insert: true}) + lastElemId = `${opNum}@${actor}` + opNum += 1 + } + } else if (op.action === 'del' && op.multiOp > 1) { + if (op.pred.length !== 1) throw new RangeError('multiOp deletion must have exactly one pred') + const startElemId = parseOpId(op.elemId), startPred = parseOpId(op.pred[0]) + for (let i = 0; i < op.multiOp; i++) { + const elemId = `${startElemId.counter + i}@${startElemId.actorId}` + const pred = [`${startPred.counter + i}@${startPred.actorId}`] + expandedOps.push({action: 'del', obj: op.obj, elemId, pred}) + opNum += 1 + } + } else { + expandedOps.push(op) + opNum += 1 + } + } + return expandedOps } -function undestroy() { - if (this._readableState) { - this._readableState.destroyed = false; - this._readableState.reading = false; - this._readableState.ended = false; - this._readableState.endEmitted = false; +/** + * Takes a change as decoded by `decodeColumns`, and changes it into the form + * expected by the rest of the backend. If `forDocument` is true, we use the op + * structure of a whole document, otherwise we use the op structure for an + * individual change. + */ +function decodeOps(ops, forDocument) { + const newOps = [] + for (let op of ops) { + const obj = (op.objCtr === null) ? '_root' : `${op.objCtr}@${op.objActor}` + const elemId = op.keyStr ? undefined : (op.keyCtr === 0 ? '_head' : `${op.keyCtr}@${op.keyActor}`) + const action = ACTIONS[op.action] || op.action + const newOp = elemId ? {obj, elemId, action} : {obj, key: op.keyStr, action} + newOp.insert = !!op.insert + if (ACTIONS[op.action] === 'set' || ACTIONS[op.action] === 'inc') { + newOp.value = op.valLen + if (op.valLen_datatype) newOp.datatype = op.valLen_datatype + } + if (!!op.chldCtr !== !!op.chldActor) { + throw new RangeError(`Mismatched child columns: ${op.chldCtr} and ${op.chldActor}`) + } + if (op.chldCtr !== null) newOp.child = `${op.chldCtr}@${op.chldActor}` + if (forDocument) { + newOp.id = `${op.idCtr}@${op.idActor}` + newOp.succ = op.succNum.map(succ => `${succ.succCtr}@${succ.succActor}`) + checkSortedOpIds(op.succNum.map(succ => ({counter: succ.succCtr, actorId: succ.succActor}))) + } else { + newOp.pred = op.predNum.map(pred => `${pred.predCtr}@${pred.predActor}`) + checkSortedOpIds(op.predNum.map(pred => ({counter: pred.predCtr, actorId: pred.predActor}))) + } + newOps.push(newOp) } + return newOps +} - if (this._writableState) { - this._writableState.destroyed = false; - this._writableState.ended = false; - this._writableState.ending = false; - this._writableState.finalCalled = false; - this._writableState.prefinished = false; - this._writableState.finished = false; - this._writableState.errorEmitted = false; +/** + * Throws an exception if the opIds in the given array are not in sorted order. + */ +function checkSortedOpIds(opIds) { + let last = null + for (let opId of opIds) { + if (last && compareParsedOpIds(last, opId) !== -1) { + throw new RangeError('operation IDs are not in ascending order') + } + last = opId } } -function emitErrorNT(self, err) { - self.emit('error', err); +function encoderByColumnId(columnId) { + if ((columnId & 7) === COLUMN_TYPE.INT_DELTA) { + return new DeltaEncoder() + } else if ((columnId & 7) === COLUMN_TYPE.BOOLEAN) { + return new BooleanEncoder() + } else if ((columnId & 7) === COLUMN_TYPE.STRING_RLE) { + return new RLEEncoder('utf8') + } else if ((columnId & 7) === COLUMN_TYPE.VALUE_RAW) { + return new Encoder() + } else { + return new RLEEncoder('uint') + } } -function errorOrDestroy(stream, err) { - // We have tests that rely on errors being emitted - // in the same tick, so changing this is semver major. - // For now when you opt-in to autoDestroy we allow - // the error to be emitted nextTick. In a future - // semver major update we should change the default to this. - var rState = stream._readableState; - var wState = stream._writableState; - if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err); +function decoderByColumnId(columnId, buffer) { + if ((columnId & 7) === COLUMN_TYPE.INT_DELTA) { + return new DeltaDecoder(buffer) + } else if ((columnId & 7) === COLUMN_TYPE.BOOLEAN) { + return new BooleanDecoder(buffer) + } else if ((columnId & 7) === COLUMN_TYPE.STRING_RLE) { + return new RLEDecoder('utf8', buffer) + } else if ((columnId & 7) === COLUMN_TYPE.VALUE_RAW) { + return new Decoder(buffer) + } else { + return new RLEDecoder('uint', buffer) + } } -module.exports = { - destroy: destroy, - undestroy: undestroy, - errorOrDestroy: errorOrDestroy -}; -}).call(this)}).call(this,require('_process')) -},{"_process":377}],128:[function(require,module,exports){ -// Ported from https://github.com/mafintosh/end-of-stream with -// permission from the author, Mathias Buus (@mafintosh). -'use strict'; - -var ERR_STREAM_PREMATURE_CLOSE = require('../../../errors').codes.ERR_STREAM_PREMATURE_CLOSE; - -function once(callback) { - var called = false; - return function () { - if (called) return; - called = true; +function makeDecoders(columns, columnSpec) { + const emptyBuf = new Uint8Array(0) + let decoders = [], columnIndex = 0, specIndex = 0 - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; + while (columnIndex < columns.length || specIndex < columnSpec.length) { + if (columnIndex === columns.length || + (specIndex < columnSpec.length && columnSpec[specIndex].columnId < columns[columnIndex].columnId)) { + const {columnId, columnName} = columnSpec[specIndex] + decoders.push({columnId, columnName, decoder: decoderByColumnId(columnId, emptyBuf)}) + specIndex++ + } else if (specIndex === columnSpec.length || columns[columnIndex].columnId < columnSpec[specIndex].columnId) { + const {columnId, buffer} = columns[columnIndex] + decoders.push({columnId, decoder: decoderByColumnId(columnId, buffer)}) + columnIndex++ + } else { // columns[columnIndex].columnId === columnSpec[specIndex].columnId + const {columnId, buffer} = columns[columnIndex], {columnName} = columnSpec[specIndex] + decoders.push({columnId, columnName, decoder: decoderByColumnId(columnId, buffer)}) + columnIndex++ + specIndex++ } - - callback.apply(this, args); - }; + } + return decoders } -function noop() {} +function decodeColumns(columns, actorIds, columnSpec) { + columns = makeDecoders(columns, columnSpec) + let parsedRows = [] + while (columns.some(col => !col.decoder.done)) { + let row = {}, col = 0 + while (col < columns.length) { + const columnId = columns[col].columnId + let groupId = columnId >> 4, groupCols = 1 + while (col + groupCols < columns.length && columns[col + groupCols].columnId >> 4 === groupId) { + groupCols++ + } -function isRequest(stream) { - return stream.setHeader && typeof stream.abort === 'function'; + if (columnId % 8 === COLUMN_TYPE.GROUP_CARD) { + const values = [], count = columns[col].decoder.readValue() + for (let i = 0; i < count; i++) { + let value = {} + for (let colOffset = 1; colOffset < groupCols; colOffset++) { + decodeValueColumns(columns, col + colOffset, actorIds, value) + } + values.push(value) + } + row[columns[col].columnName] = values + col += groupCols + } else { + col += decodeValueColumns(columns, col, actorIds, row) + } + } + parsedRows.push(row) + } + return parsedRows } -function eos(stream, opts, callback) { - if (typeof opts === 'function') return eos(stream, null, opts); - if (!opts) opts = {}; - callback = once(callback || noop); - var readable = opts.readable || opts.readable !== false && stream.readable; - var writable = opts.writable || opts.writable !== false && stream.writable; - - var onlegacyfinish = function onlegacyfinish() { - if (!stream.writable) onfinish(); - }; - - var writableEnded = stream._writableState && stream._writableState.finished; - - var onfinish = function onfinish() { - writable = false; - writableEnded = true; - if (!readable) callback.call(stream); - }; +function decodeColumnInfo(decoder) { + // A number that is all 1 bits except for the bit that indicates whether a column is + // deflate-compressed. We ignore this bit when checking whether columns are sorted by ID. + const COLUMN_ID_MASK = (-1 ^ COLUMN_TYPE_DEFLATE) >>> 0 - var readableEnded = stream._readableState && stream._readableState.endEmitted; + let lastColumnId = -1, columns = [], numColumns = decoder.readUint53() + for (let i = 0; i < numColumns; i++) { + const columnId = decoder.readUint53(), bufferLen = decoder.readUint53() + if ((columnId & COLUMN_ID_MASK) <= (lastColumnId & COLUMN_ID_MASK)) { + throw new RangeError('Columns must be in ascending order') + } + lastColumnId = columnId + columns.push({columnId, bufferLen}) + } + return columns +} - var onend = function onend() { - readable = false; - readableEnded = true; - if (!writable) callback.call(stream); - }; +function encodeColumnInfo(encoder, columns) { + const nonEmptyColumns = columns.filter(column => column.encoder.buffer.byteLength > 0) + encoder.appendUint53(nonEmptyColumns.length) + for (let column of nonEmptyColumns) { + encoder.appendUint53(column.columnId) + encoder.appendUint53(column.encoder.buffer.byteLength) + } +} - var onerror = function onerror(err) { - callback.call(stream, err); - }; +function decodeChangeHeader(decoder) { + const numDeps = decoder.readUint53(), deps = [] + for (let i = 0; i < numDeps; i++) { + deps.push(bytesToHexString(decoder.readRawBytes(32))) + } + let change = { + actor: decoder.readHexString(), + seq: decoder.readUint53(), + startOp: decoder.readUint53(), + time: decoder.readInt53(), + message: decoder.readPrefixedString(), + deps + } + const actorIds = [change.actor], numActorIds = decoder.readUint53() + for (let i = 0; i < numActorIds; i++) actorIds.push(decoder.readHexString()) + change.actorIds = actorIds + return change +} - var onclose = function onclose() { - var err; +/** + * Assembles a chunk of encoded data containing a checksum, headers, and a + * series of encoded columns. Calls `encodeHeaderCallback` with an encoder that + * should be used to add the headers. The columns should be given as `columns`. + */ +function encodeContainer(chunkType, encodeContentsCallback) { + const CHECKSUM_SIZE = 4 // checksum is first 4 bytes of SHA-256 hash of the rest of the data + const HEADER_SPACE = MAGIC_BYTES.byteLength + CHECKSUM_SIZE + 1 + 5 // 1 byte type + 5 bytes length + const body = new Encoder() + // Make space for the header at the beginning of the body buffer. We will + // copy the header in here later. This is cheaper than copying the body since + // the body is likely to be much larger than the header. + body.appendRawBytes(new Uint8Array(HEADER_SPACE)) + encodeContentsCallback(body) - if (readable && !readableEnded) { - if (!stream._readableState || !stream._readableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); - return callback.call(stream, err); - } + const bodyBuf = body.buffer + const header = new Encoder() + header.appendByte(chunkType) + header.appendUint53(bodyBuf.byteLength - HEADER_SPACE) - if (writable && !writableEnded) { - if (!stream._writableState || !stream._writableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE(); - return callback.call(stream, err); - } - }; + // Compute the hash over chunkType, length, and body + const headerBuf = header.buffer + const sha256 = new Hash() + sha256.update(headerBuf) + sha256.update(bodyBuf.subarray(HEADER_SPACE)) + const hash = sha256.digest(), checksum = hash.subarray(0, CHECKSUM_SIZE) - var onrequest = function onrequest() { - stream.req.on('finish', onfinish); - }; + // Copy header into the body buffer so that they are contiguous + bodyBuf.set(MAGIC_BYTES, HEADER_SPACE - headerBuf.byteLength - CHECKSUM_SIZE - MAGIC_BYTES.byteLength) + bodyBuf.set(checksum, HEADER_SPACE - headerBuf.byteLength - CHECKSUM_SIZE) + bodyBuf.set(headerBuf, HEADER_SPACE - headerBuf.byteLength) + return {hash, bytes: bodyBuf.subarray(HEADER_SPACE - headerBuf.byteLength - CHECKSUM_SIZE - MAGIC_BYTES.byteLength)} +} - if (isRequest(stream)) { - stream.on('complete', onfinish); - stream.on('abort', onclose); - if (stream.req) onrequest();else stream.on('request', onrequest); - } else if (writable && !stream._writableState) { - // legacy streams - stream.on('end', onlegacyfinish); - stream.on('close', onlegacyfinish); +function decodeContainerHeader(decoder, computeHash) { + if (!equalBytes(decoder.readRawBytes(MAGIC_BYTES.byteLength), MAGIC_BYTES)) { + throw new RangeError('Data does not begin with magic bytes 85 6f 4a 83') } + const expectedHash = decoder.readRawBytes(4) + const hashStartOffset = decoder.offset + const chunkType = decoder.readByte() + const chunkLength = decoder.readUint53() + const header = {chunkType, chunkLength, chunkData: decoder.readRawBytes(chunkLength)} - stream.on('end', onend); - stream.on('finish', onfinish); - if (opts.error !== false) stream.on('error', onerror); - stream.on('close', onclose); - return function () { - stream.removeListener('complete', onfinish); - stream.removeListener('abort', onclose); - stream.removeListener('request', onrequest); - if (stream.req) stream.req.removeListener('finish', onfinish); - stream.removeListener('end', onlegacyfinish); - stream.removeListener('close', onlegacyfinish); - stream.removeListener('finish', onfinish); - stream.removeListener('end', onend); - stream.removeListener('error', onerror); - stream.removeListener('close', onclose); - }; + if (computeHash) { + const sha256 = new Hash() + sha256.update(decoder.buf.subarray(hashStartOffset, decoder.offset)) + const binaryHash = sha256.digest() + if (!equalBytes(binaryHash.subarray(0, 4), expectedHash)) { + throw new RangeError('checksum does not match data') + } + header.hash = bytesToHexString(binaryHash) + } + return header } -module.exports = eos; -},{"../../../errors":119}],129:[function(require,module,exports){ -module.exports = function () { - throw new Error('Readable.from is not available in the browser') -}; +function encodeChange(changeObj) { + const { changes, actorIds } = parseAllOpIds([changeObj], true) + const change = changes[0] -},{}],130:[function(require,module,exports){ -// Ported from https://github.com/mafintosh/pump with -// permission from the author, Mathias Buus (@mafintosh). -'use strict'; + const { hash, bytes } = encodeContainer(CHUNK_TYPE_CHANGE, encoder => { + if (!Array.isArray(change.deps)) throw new TypeError('deps is not an array') + encoder.appendUint53(change.deps.length) + for (let hash of change.deps.slice().sort()) { + encoder.appendRawBytes(hexStringToBytes(hash)) + } + encoder.appendHexString(change.actor) + encoder.appendUint53(change.seq) + encoder.appendUint53(change.startOp) + encoder.appendInt53(change.time) + encoder.appendPrefixedString(change.message || '') + encoder.appendUint53(actorIds.length - 1) + for (let actor of actorIds.slice(1)) encoder.appendHexString(actor) -var eos; + const columns = encodeOps(change.ops, false) + encodeColumnInfo(encoder, columns) + for (let column of columns) encoder.appendRawBytes(column.encoder.buffer) + if (change.extraBytes) encoder.appendRawBytes(change.extraBytes) + }) -function once(callback) { - var called = false; - return function () { - if (called) return; - called = true; - callback.apply(void 0, arguments); - }; + const hexHash = bytesToHexString(hash) + if (changeObj.hash && changeObj.hash !== hexHash) { + throw new RangeError(`Change hash does not match encoding: ${changeObj.hash} != ${hexHash}`) + } + return (bytes.byteLength >= DEFLATE_MIN_SIZE) ? deflateChange(bytes) : bytes } -var _require$codes = require('../../../errors').codes, - ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS, - ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED; +function decodeChangeColumns(buffer) { + if (buffer[8] === CHUNK_TYPE_DEFLATE) buffer = inflateChange(buffer) + const decoder = new Decoder(buffer) + const header = decodeContainerHeader(decoder, true) + const chunkDecoder = new Decoder(header.chunkData) + if (!decoder.done) throw new RangeError('Encoded change has trailing data') + if (header.chunkType !== CHUNK_TYPE_CHANGE) throw new RangeError(`Unexpected chunk type: ${header.chunkType}`) -function noop(err) { - // Rethrow the error if it exists to avoid swallowing it - if (err) throw err; -} + const change = decodeChangeHeader(chunkDecoder) + const columns = decodeColumnInfo(chunkDecoder) + for (let i = 0; i < columns.length; i++) { + if ((columns[i].columnId & COLUMN_TYPE_DEFLATE) !== 0) { + throw new RangeError('change must not contain deflated columns') + } + columns[i].buffer = chunkDecoder.readRawBytes(columns[i].bufferLen) + } + if (!chunkDecoder.done) { + const restLen = chunkDecoder.buf.byteLength - chunkDecoder.offset + change.extraBytes = chunkDecoder.readRawBytes(restLen) + } -function isRequest(stream) { - return stream.setHeader && typeof stream.abort === 'function'; + change.columns = columns + change.hash = header.hash + return change } -function destroyer(stream, reading, writing, callback) { - callback = once(callback); - var closed = false; - stream.on('close', function () { - closed = true; - }); - if (eos === undefined) eos = require('./end-of-stream'); - eos(stream, { - readable: reading, - writable: writing - }, function (err) { - if (err) return callback(err); - closed = true; - callback(); - }); - var destroyed = false; - return function (err) { - if (closed) return; - if (destroyed) return; - destroyed = true; // request.destroy just do .end - .abort is what we want - - if (isRequest(stream)) return stream.abort(); - if (typeof stream.destroy === 'function') return stream.destroy(); - callback(err || new ERR_STREAM_DESTROYED('pipe')); - }; +/** + * Decodes one change in binary format into its JS object representation. + */ +function decodeChange(buffer) { + const change = decodeChangeColumns(buffer) + change.ops = decodeOps(decodeColumns(change.columns, change.actorIds, CHANGE_COLUMNS), false) + delete change.actorIds + delete change.columns + return change } -function call(fn) { - fn(); +/** + * Decodes the header fields of a change in binary format, but does not decode + * the operations. Saves work when we only need to inspect the headers. Only + * computes the hash of the change if `computeHash` is true. + */ +function decodeChangeMeta(buffer, computeHash) { + if (buffer[8] === CHUNK_TYPE_DEFLATE) buffer = inflateChange(buffer) + const header = decodeContainerHeader(new Decoder(buffer), computeHash) + if (header.chunkType !== CHUNK_TYPE_CHANGE) { + throw new RangeError('Buffer chunk type is not a change') + } + const meta = decodeChangeHeader(new Decoder(header.chunkData)) + meta.change = buffer + if (computeHash) meta.hash = header.hash + return meta } -function pipe(from, to) { - return from.pipe(to); +/** + * Compresses a binary change using DEFLATE. + */ +function deflateChange(buffer) { + const header = decodeContainerHeader(new Decoder(buffer), false) + if (header.chunkType !== CHUNK_TYPE_CHANGE) throw new RangeError(`Unexpected chunk type: ${header.chunkType}`) + const compressed = pako.deflateRaw(header.chunkData) + const encoder = new Encoder() + encoder.appendRawBytes(buffer.subarray(0, 8)) // copy MAGIC_BYTES and checksum + encoder.appendByte(CHUNK_TYPE_DEFLATE) + encoder.appendUint53(compressed.byteLength) + encoder.appendRawBytes(compressed) + return encoder.buffer } -function popCallback(streams) { - if (!streams.length) return noop; - if (typeof streams[streams.length - 1] !== 'function') return noop; - return streams.pop(); +/** + * Decompresses a binary change that has been compressed with DEFLATE. + */ +function inflateChange(buffer) { + const header = decodeContainerHeader(new Decoder(buffer), false) + if (header.chunkType !== CHUNK_TYPE_DEFLATE) throw new RangeError(`Unexpected chunk type: ${header.chunkType}`) + const decompressed = pako.inflateRaw(header.chunkData) + const encoder = new Encoder() + encoder.appendRawBytes(buffer.subarray(0, 8)) // copy MAGIC_BYTES and checksum + encoder.appendByte(CHUNK_TYPE_CHANGE) + encoder.appendUint53(decompressed.byteLength) + encoder.appendRawBytes(decompressed) + return encoder.buffer } -function pipeline() { - for (var _len = arguments.length, streams = new Array(_len), _key = 0; _key < _len; _key++) { - streams[_key] = arguments[_key]; +/** + * Takes an Uint8Array that may contain multiple concatenated changes, and + * returns an array of subarrays, each subarray containing one change. + */ +function splitContainers(buffer) { + let decoder = new Decoder(buffer), chunks = [], startOffset = 0 + while (!decoder.done) { + decodeContainerHeader(decoder, false) + chunks.push(buffer.subarray(startOffset, decoder.offset)) + startOffset = decoder.offset } + return chunks +} - var callback = popCallback(streams); - if (Array.isArray(streams[0])) streams = streams[0]; - - if (streams.length < 2) { - throw new ERR_MISSING_ARGS('streams'); +/** + * Decodes a list of changes from the binary format into JS objects. + * `binaryChanges` is an array of `Uint8Array` objects. + */ +function decodeChanges(binaryChanges) { + let decoded = [] + for (let binaryChange of binaryChanges) { + for (let chunk of splitContainers(binaryChange)) { + if (chunk[8] === CHUNK_TYPE_DOCUMENT) { + decoded = decoded.concat(decodeDocument(chunk)) + } else if (chunk[8] === CHUNK_TYPE_CHANGE || chunk[8] === CHUNK_TYPE_DEFLATE) { + decoded.push(decodeChange(chunk)) + } else { + // ignoring chunk of unknown type + } + } } - - var error; - var destroys = streams.map(function (stream, i) { - var reading = i < streams.length - 1; - var writing = i > 0; - return destroyer(stream, reading, writing, function (err) { - if (!error) error = err; - if (err) destroys.forEach(call); - if (reading) return; - destroys.forEach(call); - callback(error); - }); - }); - return streams.reduce(pipe); + return decoded } -module.exports = pipeline; -},{"../../../errors":119,"./end-of-stream":128}],131:[function(require,module,exports){ -'use strict'; - -var ERR_INVALID_OPT_VALUE = require('../../../errors').codes.ERR_INVALID_OPT_VALUE; - -function highWaterMarkFrom(options, isDuplex, duplexKey) { - return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null; +function sortOpIds(a, b) { + if (a === b) return 0 + if (a === '_root') return -1 + if (b === '_root') return +1 + const a_ = parseOpId(a), b_ = parseOpId(b) + if (a_.counter < b_.counter) return -1 + if (a_.counter > b_.counter) return +1 + if (a_.actorId < b_.actorId) return -1 + if (a_.actorId > b_.actorId) return +1 + return 0 } -function getHighWaterMark(state, options, duplexKey, isDuplex) { - var hwm = highWaterMarkFrom(options, isDuplex, duplexKey); - - if (hwm != null) { - if (!(isFinite(hwm) && Math.floor(hwm) === hwm) || hwm < 0) { - var name = isDuplex ? duplexKey : 'highWaterMark'; - throw new ERR_INVALID_OPT_VALUE(name, hwm); +/** + * Takes a set of operations `ops` loaded from an encoded document, and + * reconstructs the changes that they originally came from. + * Does not return anything, only mutates `changes`. + */ +function groupChangeOps(changes, ops) { + let changesByActor = {} // map from actorId to array of changes by that actor + for (let change of changes) { + change.ops = [] + if (!changesByActor[change.actor]) changesByActor[change.actor] = [] + if (change.seq !== changesByActor[change.actor].length + 1) { + throw new RangeError(`Expected seq = ${changesByActor[change.actor].length + 1}, got ${change.seq}`) + } + if (change.seq > 1 && changesByActor[change.actor][change.seq - 2].maxOp > change.maxOp) { + throw new RangeError('maxOp must increase monotonically per actor') } + changesByActor[change.actor].push(change) + } - return Math.floor(hwm); - } // Default value + let opsById = {} + for (let op of ops) { + if (op.action === 'del') throw new RangeError('document should not contain del operations') + op.pred = opsById[op.id] ? opsById[op.id].pred : [] + opsById[op.id] = op + for (let succ of op.succ) { + if (!opsById[succ]) { + if (op.elemId) { + const elemId = op.insert ? op.id : op.elemId + opsById[succ] = {id: succ, action: 'del', obj: op.obj, elemId, pred: []} + } else { + opsById[succ] = {id: succ, action: 'del', obj: op.obj, key: op.key, pred: []} + } + } + opsById[succ].pred.push(op.id) + } + delete op.succ + } + for (let op of Object.values(opsById)) { + if (op.action === 'del') ops.push(op) + } + for (let op of ops) { + const { counter, actorId } = parseOpId(op.id) + const actorChanges = changesByActor[actorId] + // Binary search to find the change that should contain this operation + let left = 0, right = actorChanges.length + while (left < right) { + const index = Math.floor((left + right) / 2) + if (actorChanges[index].maxOp < counter) { + left = index + 1 + } else { + right = index + } + } + if (left >= actorChanges.length) { + throw new RangeError(`Operation ID ${op.id} outside of allowed range`) + } + actorChanges[left].ops.push(op) + } - return state.objectMode ? 16 : 16 * 1024; + for (let change of changes) { + change.ops.sort((op1, op2) => sortOpIds(op1.id, op2.id)) + change.startOp = change.maxOp - change.ops.length + 1 + delete change.maxOp + for (let i = 0; i < change.ops.length; i++) { + const op = change.ops[i], expectedId = `${change.startOp + i}@${change.actor}` + if (op.id !== expectedId) { + throw new RangeError(`Expected opId ${expectedId}, got ${op.id}`) + } + delete op.id + } + } } -module.exports = { - getHighWaterMark: getHighWaterMark -}; -},{"../../../errors":119}],132:[function(require,module,exports){ -module.exports = require('events').EventEmitter; - -},{"events":201}],133:[function(require,module,exports){ -exports = module.exports = require('./lib/_stream_readable.js'); -exports.Stream = exports; -exports.Readable = exports; -exports.Writable = require('./lib/_stream_writable.js'); -exports.Duplex = require('./lib/_stream_duplex.js'); -exports.Transform = require('./lib/_stream_transform.js'); -exports.PassThrough = require('./lib/_stream_passthrough.js'); -exports.finished = require('./lib/internal/streams/end-of-stream.js'); -exports.pipeline = require('./lib/internal/streams/pipeline.js'); +function decodeDocumentChanges(changes, expectedHeads) { + let heads = {} // change hashes that are not a dependency of any other change + for (let i = 0; i < changes.length; i++) { + let change = changes[i] + change.deps = [] + for (let index of change.depsNum.map(d => d.depsIndex)) { + if (!changes[index] || !changes[index].hash) { + throw new RangeError(`No hash for index ${index} while processing index ${i}`) + } + const hash = changes[index].hash + change.deps.push(hash) + if (heads[hash]) delete heads[hash] + } + change.deps.sort() + delete change.depsNum -},{"./lib/_stream_duplex.js":120,"./lib/_stream_passthrough.js":121,"./lib/_stream_readable.js":122,"./lib/_stream_transform.js":123,"./lib/_stream_writable.js":124,"./lib/internal/streams/end-of-stream.js":128,"./lib/internal/streams/pipeline.js":130}],134:[function(require,module,exports){ -/*! safe-buffer. MIT License. Feross Aboukhadijeh */ -/* eslint-disable node/no-deprecated-api */ -var buffer = require('buffer') -var Buffer = buffer.Buffer + if (change.extraLen_datatype !== VALUE_TYPE.BYTES) { + throw new RangeError(`Bad datatype for extra bytes: ${VALUE_TYPE.BYTES}`) + } + change.extraBytes = change.extraLen + delete change.extraLen_datatype -// alternative to using Object.keys for old browsers -function copyProps (src, dst) { - for (var key in src) { - dst[key] = src[key] + // Encoding and decoding again to compute the hash of the change + changes[i] = decodeChange(encodeChange(change)) + heads[changes[i].hash] = true } -} -if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { - module.exports = buffer -} else { - // Copy properties from require('buffer') - copyProps(buffer, exports) - exports.Buffer = SafeBuffer -} -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) + const actualHeads = Object.keys(heads).sort() + let headsEqual = (actualHeads.length === expectedHeads.length), i = 0 + while (headsEqual && i < actualHeads.length) { + headsEqual = (actualHeads[i] === expectedHeads[i]) + i++ + } + if (!headsEqual) { + throw new RangeError(`Mismatched heads hashes: expected ${expectedHeads.join(', ')}, got ${actualHeads.join(', ')}`) + } } -SafeBuffer.prototype = Object.create(Buffer.prototype) +function encodeDocumentHeader(doc) { + const { changesColumns, opsColumns, actorIds, heads, headsIndexes, extraBytes } = doc + for (let column of changesColumns) deflateColumn(column) + for (let column of opsColumns) deflateColumn(column) -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) + return encodeContainer(CHUNK_TYPE_DOCUMENT, encoder => { + encoder.appendUint53(actorIds.length) + for (let actor of actorIds) { + encoder.appendHexString(actor) + } + encoder.appendUint53(heads.length) + for (let head of heads.sort()) { + encoder.appendRawBytes(hexStringToBytes(head)) + } + encodeColumnInfo(encoder, changesColumns) + encodeColumnInfo(encoder, opsColumns) + for (let column of changesColumns) encoder.appendRawBytes(column.encoder.buffer) + for (let column of opsColumns) encoder.appendRawBytes(column.encoder.buffer) + for (let index of headsIndexes) encoder.appendUint53(index) + if (extraBytes) encoder.appendRawBytes(extraBytes) + }).bytes +} -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') +function decodeDocumentHeader(buffer) { + const documentDecoder = new Decoder(buffer) + const header = decodeContainerHeader(documentDecoder, true) + const decoder = new Decoder(header.chunkData) + if (!documentDecoder.done) throw new RangeError('Encoded document has trailing data') + if (header.chunkType !== CHUNK_TYPE_DOCUMENT) throw new RangeError(`Unexpected chunk type: ${header.chunkType}`) + + const actorIds = [], numActors = decoder.readUint53() + for (let i = 0; i < numActors; i++) { + actorIds.push(decoder.readHexString()) + } + const heads = [], headsIndexes = [], numHeads = decoder.readUint53() + for (let i = 0; i < numHeads; i++) { + heads.push(bytesToHexString(decoder.readRawBytes(32))) } - return Buffer(arg, encodingOrOffset, length) -} -SafeBuffer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') + const changesColumns = decodeColumnInfo(decoder) + const opsColumns = decodeColumnInfo(decoder) + for (let i = 0; i < changesColumns.length; i++) { + changesColumns[i].buffer = decoder.readRawBytes(changesColumns[i].bufferLen) + inflateColumn(changesColumns[i]) } - var buf = Buffer(size) - if (fill !== undefined) { - if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - } else { - buf.fill(0) + for (let i = 0; i < opsColumns.length; i++) { + opsColumns[i].buffer = decoder.readRawBytes(opsColumns[i].bufferLen) + inflateColumn(opsColumns[i]) } - return buf + if (!decoder.done) { + for (let i = 0; i < numHeads; i++) headsIndexes.push(decoder.readUint53()) + } + + const extraBytes = decoder.readRawBytes(decoder.buf.byteLength - decoder.offset) + return { changesColumns, opsColumns, actorIds, heads, headsIndexes, extraBytes } } -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return Buffer(size) +function decodeDocument(buffer) { + const { changesColumns, opsColumns, actorIds, heads } = decodeDocumentHeader(buffer) + const changes = decodeColumns(changesColumns, actorIds, DOCUMENT_COLUMNS) + const ops = decodeOps(decodeColumns(opsColumns, actorIds, DOC_OPS_COLUMNS), true) + groupChangeOps(changes, ops) + decodeDocumentChanges(changes, heads) + return changes } -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') +/** + * DEFLATE-compresses the given column if it is large enough to make the compression worthwhile. + */ +function deflateColumn(column) { + if (column.encoder.buffer.byteLength >= DEFLATE_MIN_SIZE) { + column.encoder = {buffer: pako.deflateRaw(column.encoder.buffer)} + column.columnId |= COLUMN_TYPE_DEFLATE } - return buffer.SlowBuffer(size) } -},{"buffer":135}],135:[function(require,module,exports){ -(function (Buffer){(function (){ -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT +/** + * Decompresses the given column if it is DEFLATE-compressed. */ -/* eslint-disable no-proto */ - -'use strict' - -var base64 = require('base64-js') -var ieee754 = require('ieee754') - -exports.Buffer = Buffer -exports.SlowBuffer = SlowBuffer -exports.INSPECT_MAX_BYTES = 50 +function inflateColumn(column) { + if ((column.columnId & COLUMN_TYPE_DEFLATE) !== 0) { + column.buffer = pako.inflateRaw(column.buffer) + column.columnId ^= COLUMN_TYPE_DEFLATE + } +} -var K_MAX_LENGTH = 0x7fffffff -exports.kMaxLength = K_MAX_LENGTH +module.exports = { + COLUMN_TYPE, VALUE_TYPE, ACTIONS, OBJECT_TYPE, DOC_OPS_COLUMNS, CHANGE_COLUMNS, DOCUMENT_COLUMNS, + encoderByColumnId, decoderByColumnId, makeDecoders, decodeValue, + splitContainers, encodeChange, decodeChangeColumns, decodeChange, decodeChangeMeta, decodeChanges, + encodeDocumentHeader, decodeDocumentHeader, decodeDocument +} +},{"../src/common":101,"./encoding":85,"fast-sha256":127,"pako":165}],85:[function(require,module,exports){ /** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Print warning and recommend using `buffer` v4.x which has an Object - * implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * We report that the browser does not support typed arrays if the are not subclassable - * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` - * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support - * for __proto__ and has a buggy typed array implementation. + * UTF-8 decoding and encoding using API that is supported in Node >= 12 and modern browsers: + * https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encode + * https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode + * If you're running in an environment where it's not available, please use a polyfill, such as: + * https://github.com/anonyco/FastestSmallestTextEncoderDecoder */ -Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() +const utf8encoder = new TextEncoder() +const utf8decoder = new TextDecoder('utf-8') -if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && - typeof console.error === 'function') { - console.error( - 'This browser lacks typed array (Uint8Array) support which is required by ' + - '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' - ) +function stringToUtf8(string) { + return utf8encoder.encode(string) } -function typedArraySupport () { - // Can typed array instances can be augmented? - try { - var arr = new Uint8Array(1) - arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } } - return arr.foo() === 42 - } catch (e) { - return false - } +function utf8ToString(buffer) { + return utf8decoder.decode(buffer) } -Object.defineProperty(Buffer.prototype, 'parent', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.buffer +/** + * Converts a string consisting of hexadecimal digits into an Uint8Array. + */ +function hexStringToBytes(value) { + if (typeof value !== 'string') { + throw new TypeError('value is not a string') } -}) - -Object.defineProperty(Buffer.prototype, 'offset', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined - return this.byteOffset + if (!/^([0-9a-f][0-9a-f])*$/.test(value)) { + throw new RangeError('value is not hexadecimal') } -}) - -function createBuffer (length) { - if (length > K_MAX_LENGTH) { - throw new RangeError('The value "' + length + '" is invalid for option "size"') + if (value === '') { + return new Uint8Array(0) + } else { + return new Uint8Array(value.match(/../g).map(b => parseInt(b, 16))) } - // Return an augmented `Uint8Array` instance - var buf = new Uint8Array(length) - buf.__proto__ = Buffer.prototype - return buf +} + +const NIBBLE_TO_HEX = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] +const BYTE_TO_HEX = new Array(256) +for (let i = 0; i < 256; i++) { + BYTE_TO_HEX[i] = `${NIBBLE_TO_HEX[(i >>> 4) & 0xf]}${NIBBLE_TO_HEX[i & 0xf]}`; } /** - * The Buffer constructor returns instances of `Uint8Array` that have their - * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of - * `Uint8Array`, so the returned instances will have all the node `Buffer` methods - * and the `Uint8Array` methods. Square bracket notation works as expected -- it - * returns a single octet. - * - * The `Uint8Array` prototype remains unmodified. + * Converts a Uint8Array into the equivalent hexadecimal string. */ - -function Buffer (arg, encodingOrOffset, length) { - // Common case. - if (typeof arg === 'number') { - if (typeof encodingOrOffset === 'string') { - throw new TypeError( - 'The "string" argument must be of type string. Received type number' - ) - } - return allocUnsafe(arg) +function bytesToHexString(bytes) { + let hex = '', len = bytes.byteLength + for (let i = 0; i < len; i++) { + hex += BYTE_TO_HEX[bytes[i]] } - return from(arg, encodingOrOffset, length) + return hex } -// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 -if (typeof Symbol !== 'undefined' && Symbol.species != null && - Buffer[Symbol.species] === Buffer) { - Object.defineProperty(Buffer, Symbol.species, { - value: null, - configurable: true, - enumerable: false, - writable: false - }) -} +/** + * Wrapper around an Uint8Array that allows values to be appended to the buffer, + * and that automatically grows the buffer when space runs out. + */ +class Encoder { + constructor() { + this.buf = new Uint8Array(16) + this.offset = 0 + } -Buffer.poolSize = 8192 // not used by this implementation + /** + * Returns the byte array containing the encoded data. + */ + get buffer() { + this.finish() + return this.buf.subarray(0, this.offset) + } -function from (value, encodingOrOffset, length) { - if (typeof value === 'string') { - return fromString(value, encodingOrOffset) + /** + * Reallocates the encoder's buffer to be bigger. + */ + grow(minSize = 0) { + let newSize = this.buf.byteLength * 4 + while (newSize < minSize) newSize *= 2 + const newBuf = new Uint8Array(newSize) + newBuf.set(this.buf, 0) + this.buf = newBuf + return this } - if (ArrayBuffer.isView(value)) { - return fromArrayLike(value) + /** + * Appends one byte (0 to 255) to the buffer. + */ + appendByte(value) { + if (this.offset >= this.buf.byteLength) this.grow() + this.buf[this.offset] = value + this.offset += 1 } - if (value == null) { - throw TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) + /** + * Encodes a 32-bit nonnegative integer in a variable number of bytes using + * the LEB128 encoding scheme (https://en.wikipedia.org/wiki/LEB128) and + * appends it to the buffer. Returns the number of bytes written. + */ + appendUint32(value) { + if (!Number.isInteger(value)) throw new RangeError('value is not an integer') + if (value < 0 || value > 0xffffffff) throw new RangeError('number out of range') + + const numBytes = Math.max(1, Math.ceil((32 - Math.clz32(value)) / 7)) + if (this.offset + numBytes > this.buf.byteLength) this.grow() + + for (let i = 0; i < numBytes; i++) { + this.buf[this.offset + i] = (value & 0x7f) | (i === numBytes - 1 ? 0x00 : 0x80) + value >>>= 7 // zero-filling right shift + } + this.offset += numBytes + return numBytes } - if (isInstance(value, ArrayBuffer) || - (value && isInstance(value.buffer, ArrayBuffer))) { - return fromArrayBuffer(value, encodingOrOffset, length) + /** + * Encodes a 32-bit signed integer in a variable number of bytes using the + * LEB128 encoding scheme (https://en.wikipedia.org/wiki/LEB128) and appends + * it to the buffer. Returns the number of bytes written. + */ + appendInt32(value) { + if (!Number.isInteger(value)) throw new RangeError('value is not an integer') + if (value < -0x80000000 || value > 0x7fffffff) throw new RangeError('number out of range') + + const numBytes = Math.ceil((33 - Math.clz32(value >= 0 ? value : -value - 1)) / 7) + if (this.offset + numBytes > this.buf.byteLength) this.grow() + + for (let i = 0; i < numBytes; i++) { + this.buf[this.offset + i] = (value & 0x7f) | (i === numBytes - 1 ? 0x00 : 0x80) + value >>= 7 // sign-propagating right shift + } + this.offset += numBytes + return numBytes } - if (typeof value === 'number') { - throw new TypeError( - 'The "value" argument must not be of type number. Received type number' - ) + /** + * Encodes a nonnegative integer in a variable number of bytes using the LEB128 + * encoding scheme, up to the maximum size of integers supported by JavaScript + * (53 bits). + */ + appendUint53(value) { + if (!Number.isInteger(value)) throw new RangeError('value is not an integer') + if (value < 0 || value > Number.MAX_SAFE_INTEGER) { + throw new RangeError('number out of range') + } + const high32 = Math.floor(value / 0x100000000) + const low32 = (value & 0xffffffff) >>> 0 // right shift to interpret as unsigned + return this.appendUint64(high32, low32) } - var valueOf = value.valueOf && value.valueOf() - if (valueOf != null && valueOf !== value) { - return Buffer.from(valueOf, encodingOrOffset, length) + /** + * Encodes a signed integer in a variable number of bytes using the LEB128 + * encoding scheme, up to the maximum size of integers supported by JavaScript + * (53 bits). + */ + appendInt53(value) { + if (!Number.isInteger(value)) throw new RangeError('value is not an integer') + if (value < Number.MIN_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER) { + throw new RangeError('number out of range') + } + const high32 = Math.floor(value / 0x100000000) + const low32 = (value & 0xffffffff) >>> 0 // right shift to interpret as unsigned + return this.appendInt64(high32, low32) } - var b = fromObject(value) - if (b) return b + /** + * Encodes a 64-bit nonnegative integer in a variable number of bytes using + * the LEB128 encoding scheme, and appends it to the buffer. The number is + * given as two 32-bit halves since JavaScript cannot accurately represent + * integers with more than 53 bits in a single variable. + */ + appendUint64(high32, low32) { + if (!Number.isInteger(high32) || !Number.isInteger(low32)) { + throw new RangeError('value is not an integer') + } + if (high32 < 0 || high32 > 0xffffffff || low32 < 0 || low32 > 0xffffffff) { + throw new RangeError('number out of range') + } + if (high32 === 0) return this.appendUint32(low32) - if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && - typeof value[Symbol.toPrimitive] === 'function') { - return Buffer.from( - value[Symbol.toPrimitive]('string'), encodingOrOffset, length - ) + const numBytes = Math.ceil((64 - Math.clz32(high32)) / 7) + if (this.offset + numBytes > this.buf.byteLength) this.grow() + for (let i = 0; i < 4; i++) { + this.buf[this.offset + i] = (low32 & 0x7f) | 0x80 + low32 >>>= 7 // zero-filling right shift + } + this.buf[this.offset + 4] = (low32 & 0x0f) | ((high32 & 0x07) << 4) | (numBytes === 5 ? 0x00 : 0x80) + high32 >>>= 3 + for (let i = 5; i < numBytes; i++) { + this.buf[this.offset + i] = (high32 & 0x7f) | (i === numBytes - 1 ? 0x00 : 0x80) + high32 >>>= 7 + } + this.offset += numBytes + return numBytes } - throw new TypeError( - 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + - 'or Array-like Object. Received type ' + (typeof value) - ) -} + /** + * Encodes a 64-bit signed integer in a variable number of bytes using the + * LEB128 encoding scheme, and appends it to the buffer. The number is given + * as two 32-bit halves since JavaScript cannot accurately represent integers + * with more than 53 bits in a single variable. The sign of the 64-bit + * number is determined by the sign of the `high32` half; the sign of the + * `low32` half is ignored. + */ + appendInt64(high32, low32) { + if (!Number.isInteger(high32) || !Number.isInteger(low32)) { + throw new RangeError('value is not an integer') + } + if (high32 < -0x80000000 || high32 > 0x7fffffff || low32 < -0x80000000 || low32 > 0xffffffff) { + throw new RangeError('number out of range') + } + low32 >>>= 0 // interpret as unsigned + if (high32 === 0 && low32 <= 0x7fffffff) return this.appendInt32(low32) + if (high32 === -1 && low32 >= 0x80000000) return this.appendInt32(low32 - 0x100000000) -/** - * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError - * if value is a number. - * Buffer.from(str[, encoding]) - * Buffer.from(array) - * Buffer.from(buffer) - * Buffer.from(arrayBuffer[, byteOffset[, length]]) - **/ -Buffer.from = function (value, encodingOrOffset, length) { - return from(value, encodingOrOffset, length) -} + const numBytes = Math.ceil((65 - Math.clz32(high32 >= 0 ? high32 : -high32 - 1)) / 7) + if (this.offset + numBytes > this.buf.byteLength) this.grow() + for (let i = 0; i < 4; i++) { + this.buf[this.offset + i] = (low32 & 0x7f) | 0x80 + low32 >>>= 7 // zero-filling right shift + } + this.buf[this.offset + 4] = (low32 & 0x0f) | ((high32 & 0x07) << 4) | (numBytes === 5 ? 0x00 : 0x80) + high32 >>= 3 // sign-propagating right shift + for (let i = 5; i < numBytes; i++) { + this.buf[this.offset + i] = (high32 & 0x7f) | (i === numBytes - 1 ? 0x00 : 0x80) + high32 >>= 7 + } + this.offset += numBytes + return numBytes + } -// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: -// https://github.com/feross/buffer/pull/148 -Buffer.prototype.__proto__ = Uint8Array.prototype -Buffer.__proto__ = Uint8Array + /** + * Appends the contents of byte buffer `data` to the buffer. Returns the + * number of bytes appended. + */ + appendRawBytes(data) { + if (this.offset + data.byteLength > this.buf.byteLength) { + this.grow(this.offset + data.byteLength) + } + this.buf.set(data, this.offset) + this.offset += data.byteLength + return data.byteLength + } -function assertSize (size) { - if (typeof size !== 'number') { - throw new TypeError('"size" argument must be of type number') - } else if (size < 0) { - throw new RangeError('The value "' + size + '" is invalid for option "size"') + /** + * Appends a UTF-8 string to the buffer, without any metadata. Returns the + * number of bytes appended. + */ + appendRawString(value) { + if (typeof value !== 'string') throw new TypeError('value is not a string') + return this.appendRawBytes(stringToUtf8(value)) } -} -function alloc (size, fill, encoding) { - assertSize(size) - if (size <= 0) { - return createBuffer(size) + /** + * Appends the contents of byte buffer `data` to the buffer, prefixed with the + * number of bytes in the buffer (as a LEB128-encoded unsigned integer). + */ + appendPrefixedBytes(data) { + this.appendUint53(data.byteLength) + this.appendRawBytes(data) + return this } - if (fill !== undefined) { - // Only pay attention to encoding if it's a string. This - // prevents accidentally sending in a number that would - // be interpretted as a start offset. - return typeof encoding === 'string' - ? createBuffer(size).fill(fill, encoding) - : createBuffer(size).fill(fill) + + /** + * Appends a UTF-8 string to the buffer, prefixed with its length in bytes + * (where the length is encoded as an unsigned LEB128 integer). + */ + appendPrefixedString(value) { + if (typeof value !== 'string') throw new TypeError('value is not a string') + this.appendPrefixedBytes(stringToUtf8(value)) + return this } - return createBuffer(size) -} -/** - * Creates a new filled Buffer instance. - * alloc(size[, fill[, encoding]]) - **/ -Buffer.alloc = function (size, fill, encoding) { - return alloc(size, fill, encoding) -} + /** + * Takes a value, which must be a string consisting only of hexadecimal + * digits, maps it to a byte array, and appends it to the buffer, prefixed + * with its length in bytes. + */ + appendHexString(value) { + this.appendPrefixedBytes(hexStringToBytes(value)) + return this + } -function allocUnsafe (size) { - assertSize(size) - return createBuffer(size < 0 ? 0 : checked(size) | 0) + /** + * Flushes any unwritten data to the buffer. Call this before reading from + * the buffer constructed by this Encoder. + */ + finish() { + } } /** - * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. - * */ -Buffer.allocUnsafe = function (size) { - return allocUnsafe(size) -} -/** - * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. - */ -Buffer.allocUnsafeSlow = function (size) { - return allocUnsafe(size) -} - -function fromString (string, encoding) { - if (typeof encoding !== 'string' || encoding === '') { - encoding = 'utf8' + * Counterpart to Encoder. Wraps a Uint8Array buffer with a cursor indicating + * the current decoding position, and allows values to be incrementally read by + * decoding the bytes at the current position. + */ +class Decoder { + constructor(buffer) { + if (!(buffer instanceof Uint8Array)) { + throw new TypeError(`Not a byte array: ${buffer}`) + } + this.buf = buffer + this.offset = 0 } - if (!Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) + /** + * Returns false if there is still data to be read at the current decoding + * position, and true if we are at the end of the buffer. + */ + get done() { + return this.offset === this.buf.byteLength } - var length = byteLength(string, encoding) | 0 - var buf = createBuffer(length) - - var actual = buf.write(string, encoding) + /** + * Resets the cursor position, so that the next read goes back to the + * beginning of the buffer. + */ + reset() { + this.offset = 0 + } - if (actual !== length) { - // Writing a hex string, for example, that contains invalid characters will - // cause everything after the first invalid character to be ignored. (e.g. - // 'abxxcd' will be treated as 'ab') - buf = buf.slice(0, actual) + /** + * Moves the current decoding position forward by the specified number of + * bytes, without decoding anything. + */ + skip(bytes) { + if (this.offset + bytes > this.buf.byteLength) { + throw new RangeError('cannot skip beyond end of buffer') + } + this.offset += bytes } - return buf -} + /** + * Reads one byte (0 to 255) from the buffer. + */ + readByte() { + this.offset += 1 + return this.buf[this.offset - 1] + } -function fromArrayLike (array) { - var length = array.length < 0 ? 0 : checked(array.length) | 0 - var buf = createBuffer(length) - for (var i = 0; i < length; i += 1) { - buf[i] = array[i] & 255 + /** + * Reads a LEB128-encoded unsigned integer from the current position in the buffer. + * Throws an exception if the value doesn't fit in a 32-bit unsigned int. + */ + readUint32() { + let result = 0, shift = 0 + while (this.offset < this.buf.byteLength) { + const nextByte = this.buf[this.offset] + if (shift === 28 && (nextByte & 0xf0) !== 0) { // more than 5 bytes, or value > 0xffffffff + throw new RangeError('number out of range') + } + result = (result | (nextByte & 0x7f) << shift) >>> 0 // right shift to interpret value as unsigned + shift += 7 + this.offset++ + if ((nextByte & 0x80) === 0) return result + } + throw new RangeError('buffer ended with incomplete number') } - return buf -} -function fromArrayBuffer (array, byteOffset, length) { - if (byteOffset < 0 || array.byteLength < byteOffset) { - throw new RangeError('"offset" is outside of buffer bounds') + /** + * Reads a LEB128-encoded signed integer from the current position in the buffer. + * Throws an exception if the value doesn't fit in a 32-bit signed int. + */ + readInt32() { + let result = 0, shift = 0 + while (this.offset < this.buf.byteLength) { + const nextByte = this.buf[this.offset] + if ((shift === 28 && (nextByte & 0x80) !== 0) || // more than 5 bytes + (shift === 28 && (nextByte & 0x40) === 0 && (nextByte & 0x38) !== 0) || // positive int > 0x7fffffff + (shift === 28 && (nextByte & 0x40) !== 0 && (nextByte & 0x38) !== 0x38)) { // negative int < -0x80000000 + throw new RangeError('number out of range') + } + result |= (nextByte & 0x7f) << shift + shift += 7 + this.offset++ + + if ((nextByte & 0x80) === 0) { + if ((nextByte & 0x40) === 0 || shift > 28) { + return result // positive, or negative value that doesn't need sign-extending + } else { + return result | (-1 << shift) // sign-extend negative integer + } + } + } + throw new RangeError('buffer ended with incomplete number') } - if (array.byteLength < byteOffset + (length || 0)) { - throw new RangeError('"length" is outside of buffer bounds') + /** + * Reads a LEB128-encoded unsigned integer from the current position in the + * buffer. Allows any integer that can be safely represented by JavaScript + * (up to 2^53 - 1), and throws an exception outside of that range. + */ + readUint53() { + const { low32, high32 } = this.readUint64() + if (high32 < 0 || high32 > 0x1fffff) { + throw new RangeError('number out of range') + } + return high32 * 0x100000000 + low32 } - var buf - if (byteOffset === undefined && length === undefined) { - buf = new Uint8Array(array) - } else if (length === undefined) { - buf = new Uint8Array(array, byteOffset) - } else { - buf = new Uint8Array(array, byteOffset, length) + /** + * Reads a LEB128-encoded signed integer from the current position in the + * buffer. Allows any integer that can be safely represented by JavaScript + * (between -(2^53 - 1) and 2^53 - 1), throws an exception outside of that range. + */ + readInt53() { + const { low32, high32 } = this.readInt64() + if (high32 < -0x200000 || (high32 === -0x200000 && low32 === 0) || high32 > 0x1fffff) { + throw new RangeError('number out of range') + } + return high32 * 0x100000000 + low32 } - // Return an augmented `Uint8Array` instance - buf.__proto__ = Buffer.prototype - return buf -} + /** + * Reads a LEB128-encoded unsigned integer from the current position in the + * buffer. Throws an exception if the value doesn't fit in a 64-bit unsigned + * int. Returns the number in two 32-bit halves, as an object of the form + * `{high32, low32}`. + */ + readUint64() { + let low32 = 0, high32 = 0, shift = 0 + while (this.offset < this.buf.byteLength && shift <= 28) { + const nextByte = this.buf[this.offset] + low32 = (low32 | (nextByte & 0x7f) << shift) >>> 0 // right shift to interpret value as unsigned + if (shift === 28) { + high32 = (nextByte & 0x70) >>> 4 + } + shift += 7 + this.offset++ + if ((nextByte & 0x80) === 0) return { high32, low32 } + } -function fromObject (obj) { - if (Buffer.isBuffer(obj)) { - var len = checked(obj.length) | 0 - var buf = createBuffer(len) + shift = 3 + while (this.offset < this.buf.byteLength) { + const nextByte = this.buf[this.offset] + if (shift === 31 && (nextByte & 0xfe) !== 0) { // more than 10 bytes, or value > 2^64 - 1 + throw new RangeError('number out of range') + } + high32 = (high32 | (nextByte & 0x7f) << shift) >>> 0 + shift += 7 + this.offset++ + if ((nextByte & 0x80) === 0) return { high32, low32 } + } + throw new RangeError('buffer ended with incomplete number') + } - if (buf.length === 0) { - return buf + /** + * Reads a LEB128-encoded signed integer from the current position in the + * buffer. Throws an exception if the value doesn't fit in a 64-bit signed + * int. Returns the number in two 32-bit halves, as an object of the form + * `{high32, low32}`. The `low32` half is always non-negative, and the + * sign of the `high32` half indicates the sign of the 64-bit number. + */ + readInt64() { + let low32 = 0, high32 = 0, shift = 0 + while (this.offset < this.buf.byteLength && shift <= 28) { + const nextByte = this.buf[this.offset] + low32 = (low32 | (nextByte & 0x7f) << shift) >>> 0 // right shift to interpret value as unsigned + if (shift === 28) { + high32 = (nextByte & 0x70) >>> 4 + } + shift += 7 + this.offset++ + if ((nextByte & 0x80) === 0) { + if ((nextByte & 0x40) !== 0) { // sign-extend negative integer + if (shift < 32) low32 = (low32 | (-1 << shift)) >>> 0 + high32 |= -1 << Math.max(shift - 32, 0) + } + return { high32, low32 } + } } - obj.copy(buf, 0, 0, len) - return buf + shift = 3 + while (this.offset < this.buf.byteLength) { + const nextByte = this.buf[this.offset] + // On the 10th byte there are only two valid values: all 7 value bits zero + // (if the value is positive) or all 7 bits one (if the value is negative) + if (shift === 31 && nextByte !== 0 && nextByte !== 0x7f) { + throw new RangeError('number out of range') + } + high32 |= (nextByte & 0x7f) << shift + shift += 7 + this.offset++ + if ((nextByte & 0x80) === 0) { + if ((nextByte & 0x40) !== 0 && shift < 32) { // sign-extend negative integer + high32 |= -1 << shift + } + return { high32, low32 } + } + } + throw new RangeError('buffer ended with incomplete number') } - if (obj.length !== undefined) { - if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { - return createBuffer(0) + /** + * Extracts a subarray `length` bytes in size, starting from the current + * position in the buffer, and moves the position forward. + */ + readRawBytes(length) { + const start = this.offset + if (start + length > this.buf.byteLength) { + throw new RangeError('subarray exceeds buffer size') } - return fromArrayLike(obj) + this.offset += length + return this.buf.subarray(start, this.offset) } - if (obj.type === 'Buffer' && Array.isArray(obj.data)) { - return fromArrayLike(obj.data) + /** + * Extracts `length` bytes from the buffer, starting from the current position, + * and returns the UTF-8 string decoding of those bytes. + */ + readRawString(length) { + return utf8ToString(this.readRawBytes(length)) } -} -function checked (length) { - // Note: cannot use `length < K_MAX_LENGTH` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= K_MAX_LENGTH) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + - 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') + /** + * Extracts a subarray from the current position in the buffer, prefixed with + * its length in bytes (encoded as an unsigned LEB128 integer). + */ + readPrefixedBytes() { + return this.readRawBytes(this.readUint53()) } - return length | 0 -} -function SlowBuffer (length) { - if (+length != length) { // eslint-disable-line eqeqeq - length = 0 + /** + * Reads a UTF-8 string from the current position in the buffer, prefixed with its + * length in bytes (where the length is encoded as an unsigned LEB128 integer). + */ + readPrefixedString() { + return utf8ToString(this.readPrefixedBytes()) } - return Buffer.alloc(+length) -} -Buffer.isBuffer = function isBuffer (b) { - return b != null && b._isBuffer === true && - b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false + /** + * Reads a byte array from the current position in the buffer, prefixed with its + * length in bytes. Returns that byte array converted to a hexadecimal string. + */ + readHexString() { + return bytesToHexString(this.readPrefixedBytes()) + } } -Buffer.compare = function compare (a, b) { - if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) - if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) - if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { - throw new TypeError( - 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' - ) +/** + * An encoder that uses run-length encoding to compress sequences of repeated + * values. The constructor argument specifies the type of values, which may be + * either 'int', 'uint', or 'utf8'. Besides valid values of the selected + * datatype, values may also be null. + * + * The encoded buffer starts with a LEB128-encoded signed integer, the + * repetition count. The interpretation of the following values depends on this + * repetition count: + * - If this number is a positive value n, the next value in the buffer + * (encoded as the specified datatype) is repeated n times in the sequence. + * - If the repetition count is a negative value -n, then the next n values + * (encoded as the specified datatype) in the buffer are treated as a + * literal, i.e. they appear in the sequence without any further + * interpretation or repetition. + * - If the repetition count is zero, then the next value in the buffer is a + * LEB128-encoded unsigned integer indicating the number of null values + * that appear at the current position in the sequence. + * + * After one of these three has completed, the process repeats, starting again + * with a repetition count, until we reach the end of the buffer. + */ +class RLEEncoder extends Encoder { + constructor(type) { + super() + this.type = type + this.state = 'empty' + this.lastValue = undefined + this.count = 0 + this.literal = [] } - if (a === b) return 0 - - var x = a.length - var y = b.length + /** + * Appends a new value to the sequence. If `repetitions` is given, the value is repeated + * `repetitions` times. + */ + appendValue(value, repetitions = 1) { + this._appendValue(value, repetitions) + } - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i] - y = b[i] - break + /** + * Like `appendValue()`, but this method is not overridden by `DeltaEncoder`. + */ + _appendValue(value, repetitions = 1) { + if (repetitions <= 0) return + if (this.state === 'empty') { + this.state = (value === null ? 'nulls' : (repetitions === 1 ? 'loneValue' : 'repetition')) + this.lastValue = value + this.count = repetitions + } else if (this.state === 'loneValue') { + if (value === null) { + this.flush() + this.state = 'nulls' + this.count = repetitions + } else if (value === this.lastValue) { + this.state = 'repetition' + this.count = 1 + repetitions + } else if (repetitions > 1) { + this.flush() + this.state = 'repetition' + this.count = repetitions + this.lastValue = value + } else { + this.state = 'literal' + this.literal = [this.lastValue] + this.lastValue = value + } + } else if (this.state === 'repetition') { + if (value === null) { + this.flush() + this.state = 'nulls' + this.count = repetitions + } else if (value === this.lastValue) { + this.count += repetitions + } else if (repetitions > 1) { + this.flush() + this.state = 'repetition' + this.count = repetitions + this.lastValue = value + } else { + this.flush() + this.state = 'loneValue' + this.lastValue = value + } + } else if (this.state === 'literal') { + if (value === null) { + this.literal.push(this.lastValue) + this.flush() + this.state = 'nulls' + this.count = repetitions + } else if (value === this.lastValue) { + this.flush() + this.state = 'repetition' + this.count = 1 + repetitions + } else if (repetitions > 1) { + this.literal.push(this.lastValue) + this.flush() + this.state = 'repetition' + this.count = repetitions + this.lastValue = value + } else { + this.literal.push(this.lastValue) + this.lastValue = value + } + } else if (this.state === 'nulls') { + if (value === null) { + this.count += repetitions + } else if (repetitions > 1) { + this.flush() + this.state = 'repetition' + this.count = repetitions + this.lastValue = value + } else { + this.flush() + this.state = 'loneValue' + this.lastValue = value + } } } - if (x < y) return -1 - if (y < x) return 1 - return 0 -} + /** + * Copies values from the RLEDecoder `decoder` into this encoder. The `options` object may + * contain the following keys: + * - `count`: The number of values to copy. If not specified, copies all remaining values. + * - `sumValues`: If true, the function computes the sum of all numeric values as they are + * copied (null values are counted as zero), and returns that number. + * - `sumShift`: If set, values are shifted right by `sumShift` bits before adding to the sum. + * + * Returns an object of the form `{nonNullValues, sum}` where `nonNullValues` is the number of + * non-null values copied, and `sum` is the sum (only if the `sumValues` option is set). + */ + copyFrom(decoder, options = {}) { + const { count, sumValues, sumShift } = options + if (!(decoder instanceof RLEDecoder) || (decoder.type !== this.type)) { + throw new TypeError('incompatible type of decoder') + } + let remaining = (typeof count === 'number' ? count : Number.MAX_SAFE_INTEGER) + let nonNullValues = 0, sum = 0 + if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) + if (remaining === 0 || decoder.done) return sumValues ? {nonNullValues, sum} : {nonNullValues} -Buffer.isEncoding = function isEncoding (encoding) { - switch (String(encoding).toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'latin1': - case 'binary': - case 'base64': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return true - default: - return false - } -} + // Copy a value so that we have a well-defined starting state. NB: when super.copyFrom() is + // called by the DeltaEncoder subclass, the following calls to readValue() and appendValue() + // refer to the overridden methods, while later readRecord(), readRawValue() and _appendValue() + // calls refer to the non-overridden RLEDecoder/RLEEncoder methods. + let firstValue = decoder.readValue() + if (firstValue === null) { + const numNulls = Math.min(decoder.count + 1, remaining) + remaining -= numNulls + decoder.count -= numNulls - 1 + this.appendValue(null, numNulls) + if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) + if (remaining === 0 || decoder.done) return sumValues ? {nonNullValues, sum} : {nonNullValues} + firstValue = decoder.readValue() + if (firstValue === null) throw new RangeError('null run must be followed by non-null value') + } + this.appendValue(firstValue) + remaining-- + nonNullValues++ + if (sumValues) sum += (sumShift ? (firstValue >>> sumShift) : firstValue) + if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) + if (remaining === 0 || decoder.done) return sumValues ? {nonNullValues, sum} : {nonNullValues} -Buffer.concat = function concat (list, length) { - if (!Array.isArray(list)) { - throw new TypeError('"list" argument must be an Array of Buffers') - } + // Copy data at the record level without expanding repetitions + let firstRun = (decoder.count > 0) + while (remaining > 0 && !decoder.done) { + if (!firstRun) decoder.readRecord() + const numValues = Math.min(decoder.count, remaining) + decoder.count -= numValues - if (list.length === 0) { - return Buffer.alloc(0) - } + if (decoder.state === 'literal') { + nonNullValues += numValues + for (let i = 0; i < numValues; i++) { + if (decoder.done) throw new RangeError('incomplete literal') + const value = decoder.readRawValue() + if (value === decoder.lastValue) throw new RangeError('Repetition of values is not allowed in literal') + decoder.lastValue = value + this._appendValue(value) + if (sumValues) sum += (sumShift ? (value >>> sumShift) : value) + } + } else if (decoder.state === 'repetition') { + nonNullValues += numValues + if (sumValues) sum += numValues * (sumShift ? (decoder.lastValue >>> sumShift) : decoder.lastValue) + const value = decoder.lastValue + this._appendValue(value) + if (numValues > 1) { + this._appendValue(value) + if (this.state !== 'repetition') throw new RangeError(`Unexpected state ${this.state}`) + this.count += numValues - 2 + } + } else if (decoder.state === 'nulls') { + this._appendValue(null) + if (this.state !== 'nulls') throw new RangeError(`Unexpected state ${this.state}`) + this.count += numValues - 1 + } - var i - if (length === undefined) { - length = 0 - for (i = 0; i < list.length; ++i) { - length += list[i].length + firstRun = false + remaining -= numValues } + if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) + return sumValues ? {nonNullValues, sum} : {nonNullValues} } - var buffer = Buffer.allocUnsafe(length) - var pos = 0 - for (i = 0; i < list.length; ++i) { - var buf = list[i] - if (isInstance(buf, Uint8Array)) { - buf = Buffer.from(buf) - } - if (!Buffer.isBuffer(buf)) { - throw new TypeError('"list" argument must be an Array of Buffers') + /** + * Private method, do not call from outside the class. + */ + flush() { + if (this.state === 'loneValue') { + this.appendInt32(-1) + this.appendRawValue(this.lastValue) + } else if (this.state === 'repetition') { + this.appendInt53(this.count) + this.appendRawValue(this.lastValue) + } else if (this.state === 'literal') { + this.appendInt53(-this.literal.length) + for (let v of this.literal) this.appendRawValue(v) + } else if (this.state === 'nulls') { + this.appendInt32(0) + this.appendUint53(this.count) } - buf.copy(buffer, pos) - pos += buf.length + this.state = 'empty' } - return buffer -} -function byteLength (string, encoding) { - if (Buffer.isBuffer(string)) { - return string.length - } - if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { - return string.byteLength - } - if (typeof string !== 'string') { - throw new TypeError( - 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + - 'Received type ' + typeof string - ) + /** + * Private method, do not call from outside the class. + */ + appendRawValue(value) { + if (this.type === 'int') { + this.appendInt53(value) + } else if (this.type === 'uint') { + this.appendUint53(value) + } else if (this.type === 'utf8') { + this.appendPrefixedString(value) + } else { + throw new RangeError(`Unknown RLEEncoder datatype: ${this.type}`) + } } - var len = string.length - var mustMatch = (arguments.length > 2 && arguments[2] === true) - if (!mustMatch && len === 0) return 0 - - // Use a for loop to avoid recursion - var loweredCase = false - for (;;) { - switch (encoding) { - case 'ascii': - case 'latin1': - case 'binary': - return len - case 'utf8': - case 'utf-8': - return utf8ToBytes(string).length - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2 - case 'hex': - return len >>> 1 - case 'base64': - return base64ToBytes(string).length - default: - if (loweredCase) { - return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 - } - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } + /** + * Flushes any unwritten data to the buffer. Call this before reading from + * the buffer constructed by this Encoder. + */ + finish() { + if (this.state === 'literal') this.literal.push(this.lastValue) + // Don't write anything if the only values we have seen are nulls + if (this.state !== 'nulls' || this.offset > 0) this.flush() } } -Buffer.byteLength = byteLength -function slowToString (encoding, start, end) { - var loweredCase = false - - // No need to verify that "this.length <= MAX_UINT32" since it's a read-only - // property of a typed array. - - // This behaves neither like String nor Uint8Array in that we set start/end - // to their upper/lower bounds if the value passed is out of range. - // undefined is handled specially as per ECMA-262 6th Edition, - // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. - if (start === undefined || start < 0) { - start = 0 - } - // Return early if start > this.length. Done here to prevent potential uint32 - // coercion fail below. - if (start > this.length) { - return '' +/** + * Counterpart to RLEEncoder: reads values from an RLE-compressed sequence, + * returning nulls and repeated values as required. + */ +class RLEDecoder extends Decoder { + constructor(type, buffer) { + super(buffer) + this.type = type + this.lastValue = undefined + this.count = 0 + this.state = undefined } - if (end === undefined || end > this.length) { - end = this.length + /** + * Returns false if there is still data to be read at the current decoding + * position, and true if we are at the end of the buffer. + */ + get done() { + return (this.count === 0) && (this.offset === this.buf.byteLength) } - if (end <= 0) { - return '' + /** + * Resets the cursor position, so that the next read goes back to the + * beginning of the buffer. + */ + reset() { + this.offset = 0 + this.lastValue = undefined + this.count = 0 + this.state = undefined } - // Force coersion to uint32. This will also coerce falsey/NaN values to 0. - end >>>= 0 - start >>>= 0 - - if (end <= start) { - return '' + /** + * Returns the next value (or null) in the sequence. + */ + readValue() { + if (this.done) return null + if (this.count === 0) this.readRecord() + this.count -= 1 + if (this.state === 'literal') { + const value = this.readRawValue() + if (value === this.lastValue) throw new RangeError('Repetition of values is not allowed in literal') + this.lastValue = value + return value + } else { + return this.lastValue + } } - if (!encoding) encoding = 'utf8' - - while (true) { - switch (encoding) { - case 'hex': - return hexSlice(this, start, end) - - case 'utf8': - case 'utf-8': - return utf8Slice(this, start, end) - - case 'ascii': - return asciiSlice(this, start, end) - - case 'latin1': - case 'binary': - return latin1Slice(this, start, end) - - case 'base64': - return base64Slice(this, start, end) - - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return utf16leSlice(this, start, end) + /** + * Discards the next `numSkip` values in the sequence. + */ + skipValues(numSkip) { + while (numSkip > 0 && !this.done) { + if (this.count === 0) { + this.count = this.readInt53() + if (this.count > 0) { + this.lastValue = (this.count <= numSkip) ? this.skipRawValues(1) : this.readRawValue() + this.state = 'repetition' + } else if (this.count < 0) { + this.count = -this.count + this.state = 'literal' + } else { // this.count == 0 + this.count = this.readUint53() + this.lastValue = null + this.state = 'nulls' + } + } - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = (encoding + '').toLowerCase() - loweredCase = true + const consume = Math.min(numSkip, this.count) + if (this.state === 'literal') this.skipRawValues(consume) + numSkip -= consume + this.count -= consume } } -} - -// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) -// to detect a Buffer instance. It's not possible to use `instanceof Buffer` -// reliably in a browserify context because there could be multiple different -// copies of the 'buffer' package in use. This method works even for Buffer -// instances that were created from another copy of the `buffer` package. -// See: https://github.com/feross/buffer/issues/154 -Buffer.prototype._isBuffer = true - -function swap (b, n, m) { - var i = b[n] - b[n] = b[m] - b[m] = i -} -Buffer.prototype.swap16 = function swap16 () { - var len = this.length - if (len % 2 !== 0) { - throw new RangeError('Buffer size must be a multiple of 16-bits') - } - for (var i = 0; i < len; i += 2) { - swap(this, i, i + 1) + /** + * Private method, do not call from outside the class. + * Reads a repetition count from the buffer and sets up the state appropriately. + */ + readRecord() { + this.count = this.readInt53() + if (this.count > 1) { + const value = this.readRawValue() + if ((this.state === 'repetition' || this.state === 'literal') && this.lastValue === value) { + throw new RangeError('Successive repetitions with the same value are not allowed') + } + this.state = 'repetition' + this.lastValue = value + } else if (this.count === 1) { + throw new RangeError('Repetition count of 1 is not allowed, use a literal instead') + } else if (this.count < 0) { + this.count = -this.count + if (this.state === 'literal') throw new RangeError('Successive literals are not allowed') + this.state = 'literal' + } else { // this.count == 0 + if (this.state === 'nulls') throw new RangeError('Successive null runs are not allowed') + this.count = this.readUint53() + if (this.count === 0) throw new RangeError('Zero-length null runs are not allowed') + this.lastValue = null + this.state = 'nulls' + } } - return this -} -Buffer.prototype.swap32 = function swap32 () { - var len = this.length - if (len % 4 !== 0) { - throw new RangeError('Buffer size must be a multiple of 32-bits') - } - for (var i = 0; i < len; i += 4) { - swap(this, i, i + 3) - swap(this, i + 1, i + 2) + /** + * Private method, do not call from outside the class. + * Reads one value of the datatype configured on construction. + */ + readRawValue() { + if (this.type === 'int') { + return this.readInt53() + } else if (this.type === 'uint') { + return this.readUint53() + } else if (this.type === 'utf8') { + return this.readPrefixedString() + } else { + throw new RangeError(`Unknown RLEDecoder datatype: ${this.type}`) + } } - return this -} -Buffer.prototype.swap64 = function swap64 () { - var len = this.length - if (len % 8 !== 0) { - throw new RangeError('Buffer size must be a multiple of 64-bits') - } - for (var i = 0; i < len; i += 8) { - swap(this, i, i + 7) - swap(this, i + 1, i + 6) - swap(this, i + 2, i + 5) - swap(this, i + 3, i + 4) + /** + * Private method, do not call from outside the class. + * Skips over `num` values of the datatype configured on construction. + */ + skipRawValues(num) { + if (this.type === 'utf8') { + for (let i = 0; i < num; i++) this.skip(this.readUint53()) + } else { + while (num > 0 && this.offset < this.buf.byteLength) { + if ((this.buf[this.offset] & 0x80) === 0) num-- + this.offset++ + } + if (num > 0) throw new RangeError('cannot skip beyond end of buffer') + } } - return this -} - -Buffer.prototype.toString = function toString () { - var length = this.length - if (length === 0) return '' - if (arguments.length === 0) return utf8Slice(this, 0, length) - return slowToString.apply(this, arguments) -} - -Buffer.prototype.toLocaleString = Buffer.prototype.toString - -Buffer.prototype.equals = function equals (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return true - return Buffer.compare(this, b) === 0 -} - -Buffer.prototype.inspect = function inspect () { - var str = '' - var max = exports.INSPECT_MAX_BYTES - str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() - if (this.length > max) str += ' ... ' - return '' } -Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { - if (isInstance(target, Uint8Array)) { - target = Buffer.from(target, target.offset, target.byteLength) - } - if (!Buffer.isBuffer(target)) { - throw new TypeError( - 'The "target" argument must be one of type Buffer or Uint8Array. ' + - 'Received type ' + (typeof target) - ) - } - - if (start === undefined) { - start = 0 - } - if (end === undefined) { - end = target ? target.length : 0 - } - if (thisStart === undefined) { - thisStart = 0 - } - if (thisEnd === undefined) { - thisEnd = this.length - } - - if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { - throw new RangeError('out of range index') +/** + * A variant of RLEEncoder: rather than storing the actual values passed to + * appendValue(), this version stores only the first value, and for all + * subsequent values it stores the difference to the previous value. This + * encoding is good when values tend to come in sequentially incrementing runs, + * because the delta between successive values is 1, and repeated values of 1 + * are easily compressed with run-length encoding. + * + * Null values are also allowed, as with RLEEncoder. + */ +class DeltaEncoder extends RLEEncoder { + constructor() { + super('int') + this.absoluteValue = 0 } - if (thisStart >= thisEnd && start >= end) { - return 0 - } - if (thisStart >= thisEnd) { - return -1 - } - if (start >= end) { - return 1 + /** + * Appends a new integer value to the sequence. If `repetitions` is given, the value is repeated + * `repetitions` times. + */ + appendValue(value, repetitions = 1) { + if (repetitions <= 0) return + if (typeof value === 'number') { + super.appendValue(value - this.absoluteValue, 1) + this.absoluteValue = value + if (repetitions > 1) super.appendValue(0, repetitions - 1) + } else { + super.appendValue(value, repetitions) + } } - start >>>= 0 - end >>>= 0 - thisStart >>>= 0 - thisEnd >>>= 0 + /** + * Copies values from the DeltaDecoder `decoder` into this encoder. The `options` object may + * contain the key `count`, indicating the number of values to copy. If not specified, copies + * all remaining values in the decoder. + */ + copyFrom(decoder, options = {}) { + if (options.sumValues) { + throw new RangeError('unsupported options for DeltaEncoder.copyFrom()') + } + if (!(decoder instanceof DeltaDecoder)) { + throw new TypeError('incompatible type of decoder') + } - if (this === target) return 0 + let remaining = options.count + if (remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${remaining} values`) + if (remaining === 0 || decoder.done) return - var x = thisEnd - thisStart - var y = end - start - var len = Math.min(x, y) + // Copy any null values, and the first non-null value, so that appendValue() computes the + // difference between the encoder's last value and the decoder's first (absolute) value. + let value = decoder.readValue(), nulls = 0 + this.appendValue(value) + if (value === null) { + nulls = decoder.count + 1 + if (remaining !== undefined && remaining < nulls) nulls = remaining + decoder.count -= nulls - 1 + this.count += nulls - 1 + if (remaining > nulls && decoder.done) throw new RangeError(`cannot copy ${remaining} values`) + if (remaining === nulls || decoder.done) return - var thisCopy = this.slice(thisStart, thisEnd) - var targetCopy = target.slice(start, end) + // The next value read is certain to be non-null because we're not at the end of the decoder, + // and a run of nulls must be followed by a run of non-nulls. + if (decoder.count === 0) this.appendValue(decoder.readValue()) + } - for (var i = 0; i < len; ++i) { - if (thisCopy[i] !== targetCopy[i]) { - x = thisCopy[i] - y = targetCopy[i] - break + // Once we have the first value, the subsequent relative values can be copied verbatim without + // any further processing. Note that the first value copied by super.copyFrom() is an absolute + // value, while subsequent values are relative. Thus, the sum of all of the (non-null) copied + // values must equal the absolute value of the final element copied. + if (remaining !== undefined) remaining -= nulls + 1 + const { nonNullValues, sum } = super.copyFrom(decoder, {count: remaining, sumValues: true}) + if (nonNullValues > 0) { + this.absoluteValue = sum + decoder.absoluteValue = sum } } - - if (x < y) return -1 - if (y < x) return 1 - return 0 } -// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, -// OR the last index of `val` in `buffer` at offset <= `byteOffset`. -// -// Arguments: -// - buffer - a Buffer to search -// - val - a string, Buffer, or number -// - byteOffset - an index into `buffer`; will be clamped to an int32 -// - encoding - an optional encoding, relevant is val is a string -// - dir - true for indexOf, false for lastIndexOf -function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { - // Empty buffer means no match - if (buffer.length === 0) return -1 - - // Normalize byteOffset - if (typeof byteOffset === 'string') { - encoding = byteOffset - byteOffset = 0 - } else if (byteOffset > 0x7fffffff) { - byteOffset = 0x7fffffff - } else if (byteOffset < -0x80000000) { - byteOffset = -0x80000000 - } - byteOffset = +byteOffset // Coerce to Number. - if (numberIsNaN(byteOffset)) { - // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer - byteOffset = dir ? 0 : (buffer.length - 1) +/** + * Counterpart to DeltaEncoder: reads values from a delta-compressed sequence of + * numbers (may include null values). + */ +class DeltaDecoder extends RLEDecoder { + constructor(buffer) { + super('int', buffer) + this.absoluteValue = 0 } - // Normalize byteOffset: negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = buffer.length + byteOffset - if (byteOffset >= buffer.length) { - if (dir) return -1 - else byteOffset = buffer.length - 1 - } else if (byteOffset < 0) { - if (dir) byteOffset = 0 - else return -1 + /** + * Resets the cursor position, so that the next read goes back to the + * beginning of the buffer. + */ + reset() { + this.offset = 0 + this.lastValue = undefined + this.count = 0 + this.state = undefined + this.absoluteValue = 0 } - // Normalize val - if (typeof val === 'string') { - val = Buffer.from(val, encoding) + /** + * Returns the next integer (or null) value in the sequence. + */ + readValue() { + const value = super.readValue() + if (value === null) return null + this.absoluteValue += value + return this.absoluteValue } - // Finally, search either indexOf (if dir is true) or lastIndexOf - if (Buffer.isBuffer(val)) { - // Special case: looking for empty string/buffer always fails - if (val.length === 0) { - return -1 - } - return arrayIndexOf(buffer, val, byteOffset, encoding, dir) - } else if (typeof val === 'number') { - val = val & 0xFF // Search for a byte value [0-255] - if (typeof Uint8Array.prototype.indexOf === 'function') { - if (dir) { - return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) - } else { - return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + /** + * Discards the next `numSkip` values in the sequence. + */ + skipValues(numSkip) { + while (numSkip > 0 && !this.done) { + if (this.count === 0) this.readRecord() + const consume = Math.min(numSkip, this.count) + if (this.state === 'literal') { + for (let i = 0; i < consume; i++) { + this.lastValue = this.readRawValue() + this.absoluteValue += this.lastValue + } + } else if (this.state === 'repetition') { + this.absoluteValue += consume * this.lastValue } + numSkip -= consume + this.count -= consume } - return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) } - - throw new TypeError('val must be string, number or Buffer') } -function arrayIndexOf (arr, val, byteOffset, encoding, dir) { - var indexSize = 1 - var arrLength = arr.length - var valLength = val.length - - if (encoding !== undefined) { - encoding = String(encoding).toLowerCase() - if (encoding === 'ucs2' || encoding === 'ucs-2' || - encoding === 'utf16le' || encoding === 'utf-16le') { - if (arr.length < 2 || val.length < 2) { - return -1 - } - indexSize = 2 - arrLength /= 2 - valLength /= 2 - byteOffset /= 2 - } +/** + * Encodes a sequence of boolean values by mapping it to a sequence of integers: + * the number of false values, followed by the number of true values, followed + * by the number of false values, and so on. Each number is encoded as a LEB128 + * unsigned integer. This encoding is a bit like RLEEncoder, except that we + * only encode the repetition count but not the actual value, since the values + * just alternate between false and true (starting with false). + */ +class BooleanEncoder extends Encoder { + constructor() { + super() + this.lastValue = false + this.count = 0 } - function read (buf, i) { - if (indexSize === 1) { - return buf[i] + /** + * Appends a new value to the sequence. If `repetitions` is given, the value is repeated + * `repetitions` times. + */ + appendValue(value, repetitions = 1) { + if (value !== false && value !== true) { + throw new RangeError(`Unsupported value for BooleanEncoder: ${value}`) + } + if (repetitions <= 0) return + if (this.lastValue === value) { + this.count += repetitions } else { - return buf.readUInt16BE(i * indexSize) + this.appendUint53(this.count) + this.lastValue = value + this.count = repetitions } } - var i - if (dir) { - var foundIndex = -1 - for (i = byteOffset; i < arrLength; i++) { - if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { - if (foundIndex === -1) foundIndex = i - if (i - foundIndex + 1 === valLength) return foundIndex * indexSize - } else { - if (foundIndex !== -1) i -= i - foundIndex - foundIndex = -1 - } - } - } else { - if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength - for (i = byteOffset; i >= 0; i--) { - var found = true - for (var j = 0; j < valLength; j++) { - if (read(arr, i + j) !== read(val, j)) { - found = false - break - } - } - if (found) return i + /** + * Copies values from the BooleanDecoder `decoder` into this encoder. The `options` object may + * contain the key `count`, indicating the number of values to copy. If not specified, copies + * all remaining values in the decoder. + */ + copyFrom(decoder, options = {}) { + if (!(decoder instanceof BooleanDecoder)) { + throw new TypeError('incompatible type of decoder') } - } - - return -1 -} -Buffer.prototype.includes = function includes (val, byteOffset, encoding) { - return this.indexOf(val, byteOffset, encoding) !== -1 -} + const { count } = options + let remaining = (typeof count === 'number' ? count : Number.MAX_SAFE_INTEGER) + if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) + if (remaining === 0 || decoder.done) return -Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, true) -} + // Copy one value to bring decoder and encoder state into sync, then finish that value's repetitions + this.appendValue(decoder.readValue()) + remaining-- + const firstCopy = Math.min(decoder.count, remaining) + this.count += firstCopy + decoder.count -= firstCopy + remaining -= firstCopy -Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, false) -} + while (remaining > 0 && !decoder.done) { + decoder.count = decoder.readUint53() + if (decoder.count === 0) throw new RangeError('Zero-length runs are not allowed') + decoder.lastValue = !decoder.lastValue + this.appendUint53(this.count) -function hexWrite (buf, string, offset, length) { - offset = Number(offset) || 0 - var remaining = buf.length - offset - if (!length) { - length = remaining - } else { - length = Number(length) - if (length > remaining) { - length = remaining + const numCopied = Math.min(decoder.count, remaining) + this.count = numCopied + this.lastValue = decoder.lastValue + decoder.count -= numCopied + remaining -= numCopied } - } - - var strLen = string.length - if (length > strLen / 2) { - length = strLen / 2 - } - for (var i = 0; i < length; ++i) { - var parsed = parseInt(string.substr(i * 2, 2), 16) - if (numberIsNaN(parsed)) return i - buf[offset + i] = parsed + if (count && remaining > 0 && decoder.done) throw new RangeError(`cannot copy ${count} values`) } - return i -} - -function utf8Write (buf, string, offset, length) { - return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) -} -function asciiWrite (buf, string, offset, length) { - return blitBuffer(asciiToBytes(string), buf, offset, length) + /** + * Flushes any unwritten data to the buffer. Call this before reading from + * the buffer constructed by this Encoder. + */ + finish() { + if (this.count > 0) { + this.appendUint53(this.count) + this.count = 0 + } + } } -function latin1Write (buf, string, offset, length) { - return asciiWrite(buf, string, offset, length) -} +/** + * Counterpart to BooleanEncoder: reads boolean values from a runlength-encoded + * sequence. + */ +class BooleanDecoder extends Decoder { + constructor(buffer) { + super(buffer) + this.lastValue = true // is negated the first time we read a count + this.firstRun = true + this.count = 0 + } -function base64Write (buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length) -} + /** + * Returns false if there is still data to be read at the current decoding + * position, and true if we are at the end of the buffer. + */ + get done() { + return (this.count === 0) && (this.offset === this.buf.byteLength) + } -function ucs2Write (buf, string, offset, length) { - return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) -} + /** + * Resets the cursor position, so that the next read goes back to the + * beginning of the buffer. + */ + reset() { + this.offset = 0 + this.lastValue = true + this.firstRun = true + this.count = 0 + } -Buffer.prototype.write = function write (string, offset, length, encoding) { - // Buffer#write(string) - if (offset === undefined) { - encoding = 'utf8' - length = this.length - offset = 0 - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset - length = this.length - offset = 0 - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset >>> 0 - if (isFinite(length)) { - length = length >>> 0 - if (encoding === undefined) encoding = 'utf8' - } else { - encoding = length - length = undefined + /** + * Returns the next value in the sequence. + */ + readValue() { + if (this.done) return false + while (this.count === 0) { + this.count = this.readUint53() + this.lastValue = !this.lastValue + if (this.count === 0 && !this.firstRun) { + throw new RangeError('Zero-length runs are not allowed') + } + this.firstRun = false } - } else { - throw new Error( - 'Buffer.write(string, encoding, offset[, length]) is no longer supported' - ) + this.count -= 1 + return this.lastValue } - var remaining = this.length - offset - if (length === undefined || length > remaining) length = remaining - - if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { - throw new RangeError('Attempt to write outside buffer bounds') + /** + * Discards the next `numSkip` values in the sequence. + */ + skipValues(numSkip) { + while (numSkip > 0 && !this.done) { + if (this.count === 0) { + this.count = this.readUint53() + this.lastValue = !this.lastValue + if (this.count === 0) throw new RangeError('Zero-length runs are not allowed') + } + if (this.count < numSkip) { + numSkip -= this.count + this.count = 0 + } else { + this.count -= numSkip + numSkip = 0 + } + } } +} - if (!encoding) encoding = 'utf8' +module.exports = { + stringToUtf8, utf8ToString, hexStringToBytes, bytesToHexString, + Encoder, Decoder, RLEEncoder, RLEDecoder, DeltaEncoder, DeltaDecoder, BooleanEncoder, BooleanDecoder +} - var loweredCase = false - for (;;) { - switch (encoding) { - case 'hex': - return hexWrite(this, string, offset, length) +},{}],86:[function(require,module,exports){ +const { init, clone, free, applyChanges, applyLocalChange, save, load, loadChanges, getPatch, getHeads, getAllChanges, getChanges, getChangesAdded, getChangeByHash, getMissingDeps } = require("./backend") +const { receiveSyncMessage, generateSyncMessage, encodeSyncMessage, decodeSyncMessage, encodeSyncState, decodeSyncState, initSyncState } = require('./sync') - case 'utf8': - case 'utf-8': - return utf8Write(this, string, offset, length) +module.exports = { + init, clone, free, applyChanges, applyLocalChange, save, load, loadChanges, getPatch, + getHeads, getAllChanges, getChanges, getChangesAdded, getChangeByHash, getMissingDeps, + receiveSyncMessage, generateSyncMessage, encodeSyncMessage, decodeSyncMessage, encodeSyncState, decodeSyncState, initSyncState +} - case 'ascii': - return asciiWrite(this, string, offset, length) +},{"./backend":83,"./sync":88}],87:[function(require,module,exports){ +const { parseOpId, copyObject } = require('../src/common') +const { COLUMN_TYPE, VALUE_TYPE, ACTIONS, OBJECT_TYPE, DOC_OPS_COLUMNS, CHANGE_COLUMNS, DOCUMENT_COLUMNS, + encoderByColumnId, decoderByColumnId, makeDecoders, decodeValue, + encodeChange, decodeChangeColumns, decodeChangeMeta, decodeChanges, decodeDocumentHeader, encodeDocumentHeader } = require('./columnar') - case 'latin1': - case 'binary': - return latin1Write(this, string, offset, length) +const MAX_BLOCK_SIZE = 600 // operations +const BLOOM_BITS_PER_ENTRY = 10, BLOOM_NUM_PROBES = 7 // 1% false positive rate +const BLOOM_FILTER_SIZE = Math.floor(BLOOM_BITS_PER_ENTRY * MAX_BLOCK_SIZE / 8) // bytes - case 'base64': - // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length) +const objActorIdx = 0, objCtrIdx = 1, keyActorIdx = 2, keyCtrIdx = 3, keyStrIdx = 4, + idActorIdx = 5, idCtrIdx = 6, insertIdx = 7, actionIdx = 8, valLenIdx = 9, valRawIdx = 10, + predNumIdx = 13, predActorIdx = 14, predCtrIdx = 15, succNumIdx = 13, succActorIdx = 14, succCtrIdx = 15 - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return ucs2Write(this, string, offset, length) +const PRED_COLUMN_IDS = CHANGE_COLUMNS + .filter(column => ['predNum', 'predActor', 'predCtr'].includes(column.columnName)) + .map(column => column.columnId) - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) - encoding = ('' + encoding).toLowerCase() - loweredCase = true - } +/** + * Updates `objectTree`, which is a tree of nested objects, so that afterwards + * `objectTree[path[0]][path[1]][...] === value`. Only the root object is mutated, whereas any + * nested objects are copied before updating. This means that once the root object has been + * shallow-copied, this function can be used to update it without mutating the previous version. + */ +function deepCopyUpdate(objectTree, path, value) { + if (path.length === 1) { + objectTree[path[0]] = value + } else { + let child = Object.assign({}, objectTree[path[0]]) + deepCopyUpdate(child, path.slice(1), value) + objectTree[path[0]] = child } } -Buffer.prototype.toJSON = function toJSON () { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this._arr || this, 0) - } -} +/** + * Scans a block of document operations, encoded as columns `docCols`, to find the position at which + * an operation (or sequence of operations) `ops` should be applied. `actorIds` is the array that + * maps actor numbers to hexadecimal actor IDs. `resumeInsertion` is true if we're performing a list + * insertion and we already found the reference element in a previous block, but we reached the end + * of that previous block while scanning for the actual insertion position, and so we're continuing + * the scan in a subsequent block. + * + * Returns an object with keys: + * - `found`: false if we were scanning for a reference element in a list but couldn't find it; + * true otherwise. + * - `skipCount`: the number of operations, counted from the start of the block, after which the + * new operations should be inserted or applied. + * - `visibleCount`: if modifying a list object, the number of visible (i.e. non-deleted) list + * elements that precede the position where the new operations should be applied. + */ +function seekWithinBlock(ops, docCols, actorIds, resumeInsertion) { + for (let col of docCols) col.decoder.reset() + const { objActor, objCtr, keyActor, keyCtr, keyStr, idActor, idCtr, insert } = ops + const [objActorD, objCtrD, /* keyActorD */, /* keyCtrD */, keyStrD, idActorD, idCtrD, insertD, actionD, + /* valLenD */, /* valRawD */, /* chldActorD */, /* chldCtrD */, succNumD] = docCols.map(col => col.decoder) + let skipCount = 0, visibleCount = 0, elemVisible = false, nextObjActor = null, nextObjCtr = null + let nextIdActor = null, nextIdCtr = null, nextKeyStr = null, nextInsert = null, nextSuccNum = 0 -function base64Slice (buf, start, end) { - if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf) - } else { - return base64.fromByteArray(buf.slice(start, end)) + // Seek to the beginning of the object being updated + if (objCtr !== null && !resumeInsertion) { + while (!objCtrD.done || !objActorD.done || !actionD.done) { + nextObjCtr = objCtrD.readValue() + nextObjActor = actorIds[objActorD.readValue()] + actionD.skipValues(1) + if (nextObjCtr === null || !nextObjActor || nextObjCtr < objCtr || + (nextObjCtr === objCtr && nextObjActor < objActor)) { + skipCount += 1 + } else { + break + } + } + } + if ((nextObjCtr !== objCtr || nextObjActor !== objActor) && !resumeInsertion) { + return {found: true, skipCount, visibleCount} } -} - -function utf8Slice (buf, start, end) { - end = Math.min(buf.length, end) - var res = [] - var i = start - while (i < end) { - var firstByte = buf[i] - var codePoint = null - var bytesPerSequence = (firstByte > 0xEF) ? 4 - : (firstByte > 0xDF) ? 3 - : (firstByte > 0xBF) ? 2 - : 1 + // Seek to the appropriate key (if string key is used) + if (keyStr !== null) { + keyStrD.skipValues(skipCount) + while (!keyStrD.done) { + const objActorIndex = objActorD.readValue() + nextObjActor = objActorIndex === null ? null : actorIds[objActorIndex] + nextObjCtr = objCtrD.readValue() + nextKeyStr = keyStrD.readValue() + if (nextKeyStr !== null && nextKeyStr < keyStr && + nextObjCtr === objCtr && nextObjActor === objActor) { + skipCount += 1 + } else { + break + } + } + return {found: true, skipCount, visibleCount} + } - if (i + bytesPerSequence <= end) { - var secondByte, thirdByte, fourthByte, tempCodePoint + idCtrD.skipValues(skipCount) + idActorD.skipValues(skipCount) + insertD.skipValues(skipCount) + succNumD.skipValues(skipCount) + nextIdCtr = idCtrD.readValue() + nextIdActor = actorIds[idActorD.readValue()] + nextInsert = insertD.readValue() + nextSuccNum = succNumD.readValue() - switch (bytesPerSequence) { - case 1: - if (firstByte < 0x80) { - codePoint = firstByte - } - break - case 2: - secondByte = buf[i + 1] - if ((secondByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) - if (tempCodePoint > 0x7F) { - codePoint = tempCodePoint - } - } - break - case 3: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) - if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { - codePoint = tempCodePoint - } - } - break - case 4: - secondByte = buf[i + 1] - thirdByte = buf[i + 2] - fourthByte = buf[i + 3] - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) - if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { - codePoint = tempCodePoint - } - } + // If we are inserting into a list, an opId key is used, and we need to seek to a position *after* + // the referenced operation. Moreover, we need to skip over any existing operations with a greater + // opId than the new insertion, for CRDT convergence on concurrent insertions in the same place. + if (insert) { + // If insertion is not at the head, search for the reference element + if (!resumeInsertion && keyCtr !== null && keyCtr > 0 && keyActor !== null) { + skipCount += 1 + while (!idCtrD.done && !idActorD.done && (nextIdCtr !== keyCtr || nextIdActor !== keyActor)) { + if (nextInsert) elemVisible = false + if (nextSuccNum === 0 && !elemVisible) { + visibleCount += 1 + elemVisible = true + } + nextIdCtr = idCtrD.readValue() + nextIdActor = actorIds[idActorD.readValue()] + nextObjCtr = objCtrD.readValue() + nextObjActor = actorIds[objActorD.readValue()] + nextInsert = insertD.readValue() + nextSuccNum = succNumD.readValue() + if (nextObjCtr === objCtr && nextObjActor === objActor) skipCount += 1; else break + } + if (nextObjCtr !== objCtr || nextObjActor !== objActor || nextIdCtr !== keyCtr || + nextIdActor !== keyActor || !nextInsert) { + return {found: false, skipCount, visibleCount} + } + if (nextInsert) elemVisible = false + if (nextSuccNum === 0 && !elemVisible) { + visibleCount += 1 + elemVisible = true } + + // Set up the next* variables to the operation following the reference element + if (idCtrD.done || idActorD.done) return {found: true, skipCount, visibleCount} + nextIdCtr = idCtrD.readValue() + nextIdActor = actorIds[idActorD.readValue()] + nextObjCtr = objCtrD.readValue() + nextObjActor = actorIds[objActorD.readValue()] + nextInsert = insertD.readValue() + nextSuccNum = succNumD.readValue() } - if (codePoint === null) { - // we did not generate a valid codePoint so insert a - // replacement char (U+FFFD) and advance only 1 byte - codePoint = 0xFFFD - bytesPerSequence = 1 - } else if (codePoint > 0xFFFF) { - // encode to utf16 (surrogate pair dance) - codePoint -= 0x10000 - res.push(codePoint >>> 10 & 0x3FF | 0xD800) - codePoint = 0xDC00 | codePoint & 0x3FF + // Skip over any list elements with greater ID than the new one, and any non-insertions + while ((!nextInsert || nextIdCtr > idCtr || (nextIdCtr === idCtr && nextIdActor > idActor)) && + nextObjCtr === objCtr && nextObjActor === objActor) { + skipCount += 1 + if (nextInsert) elemVisible = false + if (nextSuccNum === 0 && !elemVisible) { + visibleCount += 1 + elemVisible = true + } + if (!idCtrD.done && !idActorD.done) { + nextIdCtr = idCtrD.readValue() + nextIdActor = actorIds[idActorD.readValue()] + nextObjCtr = objCtrD.readValue() + nextObjActor = actorIds[objActorD.readValue()] + nextInsert = insertD.readValue() + nextSuccNum = succNumD.readValue() + } else { + break + } } - res.push(codePoint) - i += bytesPerSequence + } else if (keyCtr !== null && keyCtr > 0 && keyActor !== null) { + // If we are updating an existing list element, seek to just before the referenced ID + while ((!nextInsert || nextIdCtr !== keyCtr || nextIdActor !== keyActor) && + nextObjCtr === objCtr && nextObjActor === objActor) { + skipCount += 1 + if (nextInsert) elemVisible = false + if (nextSuccNum === 0 && !elemVisible) { + visibleCount += 1 + elemVisible = true + } + if (!idCtrD.done && !idActorD.done) { + nextIdCtr = idCtrD.readValue() + nextIdActor = actorIds[idActorD.readValue()] + nextObjCtr = objCtrD.readValue() + nextObjActor = actorIds[objActorD.readValue()] + nextInsert = insertD.readValue() + nextSuccNum = succNumD.readValue() + } else { + break + } + } + if (nextObjCtr !== objCtr || nextObjActor !== objActor || nextIdCtr !== keyCtr || + nextIdActor !== keyActor || !nextInsert) { + return {found: false, skipCount, visibleCount} + } } - - return decodeCodePointsArray(res) + return {found: true, skipCount, visibleCount} } -// Based on http://stackoverflow.com/a/22747272/680742, the browser with -// the lowest limit is Chrome, with 0x10000 args. -// We go 1 magnitude less, for safety -var MAX_ARGUMENTS_LENGTH = 0x1000 - -function decodeCodePointsArray (codePoints) { - var len = codePoints.length - if (len <= MAX_ARGUMENTS_LENGTH) { - return String.fromCharCode.apply(String, codePoints) // avoid extra slice() - } +/** + * Returns the number of list elements that should be added to a list index when skipping over the + * block with index `blockIndex` in the list object with ID `objectId`. + */ +function visibleListElements(docState, blockIndex, objectId) { + const thisBlock = docState.blocks[blockIndex] + const nextBlock = docState.blocks[blockIndex + 1] - // Decode in chunks to avoid "call stack size exceeded". - var res = '' - var i = 0 - while (i < len) { - res += String.fromCharCode.apply( - String, - codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) - ) + let blockVisible = thisBlock.numVisible[objectId] + if (blockVisible !== undefined) { + // If a list element is split across the block boundary, don't double-count it + if (thisBlock.lastVisibleActor === nextBlock.firstVisibleActor && + thisBlock.lastVisibleActor !== undefined && + thisBlock.lastVisibleCtr === nextBlock.firstVisibleCtr && + thisBlock.lastVisibleCtr !== undefined) blockVisible -= 1 + return blockVisible + } else { + return 0 } - return res } -function asciiSlice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) +/** + * Scans the blocks of document operations to find the position where a new operation should be + * inserted. Returns an object with keys: + * - `blockIndex`: the index of the block into which we should insert the new operation + * - `skipCount`: the number of operations, counted from the start of the block, after which the + * new operations should be inserted or merged. + * - `visibleCount`: if modifying a list object, the number of visible (i.e. non-deleted) list + * elements that precede the position where the new operations should be applied. + */ +function seekToOp(docState, ops) { + const { objActor, objCtr, keyActor, keyCtr, keyStr } = ops + let blockIndex = 0, totalVisible = 0 - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i] & 0x7F) + // Skip any blocks that contain only objects with lower objectIds + if (objCtr !== null) { + while (blockIndex < docState.blocks.length - 1) { + const blockActor = docState.blocks[blockIndex].lastObjectActor === undefined ? undefined + : docState.actorIds[docState.blocks[blockIndex].lastObjectActor] + const blockCtr = docState.blocks[blockIndex].lastObjectCtr + if (blockCtr === undefined || blockCtr < objCtr || (blockCtr === objCtr && blockActor < objActor)) { + blockIndex++ + } else { + break + } + } } - return ret -} -function latin1Slice (buf, start, end) { - var ret = '' - end = Math.min(buf.length, end) + if (keyStr !== null) { + // String key is used. First skip any blocks that contain only lower keys + while (blockIndex < docState.blocks.length - 1) { + const blockLastKey = docState.blocks[blockIndex].lastKey[ops.objId] + if (blockLastKey !== undefined && blockLastKey < keyStr) blockIndex++; else break + } - for (var i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i]) - } - return ret -} + // When we have a candidate block, decode it to find the exact insertion position + const {skipCount} = seekWithinBlock(ops, docState.blocks[blockIndex].columns, docState.actorIds, false) + return {blockIndex, skipCount, visibleCount: 0} -function hexSlice (buf, start, end) { - var len = buf.length + } else { + // List operation + const insertAtHead = keyCtr === null || keyCtr === 0 || keyActor === null + const keyActorNum = keyActor === null ? null : docState.actorIds.indexOf(keyActor) + let resumeInsertion = false - if (!start || start < 0) start = 0 - if (!end || end < 0 || end > len) end = len + while (true) { + // Search for the reference element, skipping any blocks whose Bloom filter does not contain + // the reference element. We only do this if not inserting at the head (in which case there is + // no reference element), or if we already found the reference element in an earlier block (in + // which case we have resumeInsertion === true). The latter case arises with concurrent + // insertions at the same position, and so we have to scan beyond the reference element to + // find the actual insertion position, and that further scan crosses a block boundary. + if (!insertAtHead && !resumeInsertion) { + while (blockIndex < docState.blocks.length - 1 && + !bloomFilterContains(docState.blocks[blockIndex].bloom, keyActorNum, keyCtr)) { + // If we reach the end of the list object without a Bloom filter hit, the reference element + // doesn't exist + if (docState.blocks[blockIndex].lastObjectCtr > objCtr) { + throw new RangeError(`Reference element not found: ${keyCtr}@${keyActor}`) + } - var out = '' - for (var i = start; i < end; ++i) { - out += toHex(buf[i]) - } - return out -} + // Add up number of visible list elements in any blocks we skip, for list index computation + totalVisible += visibleListElements(docState, blockIndex, ops.objId) + blockIndex++ + } + } -function utf16leSlice (buf, start, end) { - var bytes = buf.slice(start, end) - var res = '' - for (var i = 0; i < bytes.length; i += 2) { - res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) - } - return res -} + // We have a candidate block. Decode it to see whether it really contains the reference element + const {found, skipCount, visibleCount} = seekWithinBlock(ops, + docState.blocks[blockIndex].columns, + docState.actorIds, + resumeInsertion) -Buffer.prototype.slice = function slice (start, end) { - var len = this.length - start = ~~start - end = end === undefined ? len : ~~end + if (blockIndex === docState.blocks.length - 1) { + // Last block: if we haven't found the reference element by now, it's an error + if (found) { + return {blockIndex, skipCount, visibleCount: totalVisible + visibleCount} + } else { + throw new RangeError(`Reference element not found: ${keyCtr}@${keyActor}`) + } - if (start < 0) { - start += len - if (start < 0) start = 0 - } else if (start > len) { - start = len - } + } else if (found && skipCount < docState.blocks[blockIndex].numOps) { + // The insertion position lies within the current block + return {blockIndex, skipCount, visibleCount: totalVisible + visibleCount} + } - if (end < 0) { - end += len - if (end < 0) end = 0 - } else if (end > len) { - end = len + // Reference element not found and there are still blocks left ==> it was probably a false positive. + // Reference element found, but we skipped all the way to the end of the block ==> we need to + // continue scanning the next block to find the actual insertion position. + // Either way, go back round the loop again to skip blocks until the next Bloom filter hit. + resumeInsertion = found && ops.insert + totalVisible += visibleListElements(docState, blockIndex, ops.objId) + blockIndex++ + } } - - if (end < start) end = start - - var newBuf = this.subarray(start, end) - // Return an augmented `Uint8Array` instance - newBuf.__proto__ = Buffer.prototype - return newBuf } -/* - * Need to make sure that buffer isn't trying to write out of bounds. +/** + * Updates Bloom filter `bloom`, given as a Uint8Array, to contain the list element ID consisting of + * counter `elemIdCtr` and actor number `elemIdActor`. We don't actually bother computing a hash + * function, since those two integers serve perfectly fine as input. We turn the two integers into a + * sequence of probe indexes using the triple hashing algorithm from the following paper: + * + * Peter C. Dillinger and Panagiotis Manolios. Bloom Filters in Probabilistic Verification. + * 5th International Conference on Formal Methods in Computer-Aided Design (FMCAD), November 2004. + * http://www.ccis.northeastern.edu/home/pete/pub/bloom-filters-verification.pdf */ -function checkOffset (offset, ext, length) { - if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') - if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') -} +function bloomFilterAdd(bloom, elemIdActor, elemIdCtr) { + let modulo = 8 * bloom.byteLength, x = elemIdCtr % modulo, y = elemIdActor % modulo -Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) + // Use one step of FNV-1a to compute a third value from the two inputs. + // Taken from http://www.isthe.com/chongo/tech/comp/fnv/index.html + // The prime is just over 2^24, so elemIdCtr can be up to about 2^29 = 500 million before the + // result of the multiplication exceeds 2^53. And even if it does exceed 2^53 and loses precision, + // that shouldn't be a problem as it should still be deterministic, and the Bloom filter + // computation only needs to be internally consistent within this library. + let z = ((elemIdCtr ^ elemIdActor) * 16777619 >>> 0) % modulo - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul + for (let i = 0; i < BLOOM_NUM_PROBES; i++) { + bloom[x >>> 3] |= 1 << (x & 7) + x = (x + y) % modulo + y = (y + z) % modulo } - - return val } -Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - checkOffset(offset, byteLength, this.length) - } +/** + * Returns true if the list element ID consisting of counter `elemIdCtr` and actor number + * `elemIdActor` is likely to be contained in the Bloom filter `bloom`. + */ +function bloomFilterContains(bloom, elemIdActor, elemIdCtr) { + let modulo = 8 * bloom.byteLength, x = elemIdCtr % modulo, y = elemIdActor % modulo + let z = ((elemIdCtr ^ elemIdActor) * 16777619 >>> 0) % modulo - var val = this[offset + --byteLength] - var mul = 1 - while (byteLength > 0 && (mul *= 0x100)) { - val += this[offset + --byteLength] * mul + // See comments in the bloomFilterAdd function for an explanation + for (let i = 0; i < BLOOM_NUM_PROBES; i++) { + if ((bloom[x >>> 3] & (1 << (x & 7))) === 0) { + return false + } + x = (x + y) % modulo + y = (y + z) % modulo } - - return val -} - -Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - return this[offset] -} - -Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return this[offset] | (this[offset + 1] << 8) -} - -Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - return (this[offset] << 8) | this[offset + 1] + return true } -Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) +/** + * Reads the relevant columns of a block of operations and updates that block to contain the + * metadata we need to efficiently figure out where to insert new operations. + */ +function updateBlockMetadata(block, actorIds) { + block.bloom = new Uint8Array(BLOOM_FILTER_SIZE) + block.lastKey = {} + block.numVisible = {} + block.numOps = 0 + block.lastObjectActor = undefined + block.lastObjectCtr = undefined + block.firstVisibleActor = undefined + block.firstVisibleCtr = undefined + block.lastVisibleActor = undefined + block.lastVisibleCtr = undefined - return ((this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16)) + - (this[offset + 3] * 0x1000000) -} + for (let col of block.columns) col.decoder.reset() + const [objActorD, objCtrD, keyActorD, keyCtrD, keyStrD, idActorD, idCtrD, insertD, /* actionD */, + /* valLenD */, /* valRawD */, /* chldActorD */, /* chldCtrD */, succNumD] = block.columns.map(col => col.decoder) -Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) + while (!idCtrD.done) { + block.numOps += 1 + const objActor = objActorD.readValue(), objCtr = objCtrD.readValue() + const keyActor = keyActorD.readValue(), keyCtr = keyCtrD.readValue(), keyStr = keyStrD.readValue() + const idActor = idActorD.readValue(), idCtr = idCtrD.readValue() + const insert = insertD.readValue(), succNum = succNumD.readValue() + const objectId = objActor === null ? '_root' : `${objCtr}@${actorIds[objActor]}` - return (this[offset] * 0x1000000) + - ((this[offset + 1] << 16) | - (this[offset + 2] << 8) | - this[offset + 3]) -} + if (objActor !== null && objCtr !== null) { + block.lastObjectActor = objActor + block.lastObjectCtr = objCtr + } -Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) + if (keyStr !== null) { + // Map key: for each object, record the highest key contained in the block + block.lastKey[objectId] = keyStr + } else if (insert || keyCtr !== null) { + // List element + if (block.numVisible[objectId] === undefined) block.numVisible[objectId] = 0 + const elemIdActor = insert ? idActor : keyActor + const elemIdCtr = insert ? idCtr : keyCtr + bloomFilterAdd(block.bloom, elemIdActor, elemIdCtr) - var val = this[offset] - var mul = 1 - var i = 0 - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul + // If the list element is visible, update the block metadata accordingly + if (succNum === 0) { + if (block.firstVisibleActor === undefined) block.firstVisibleActor = elemIdActor + if (block.firstVisibleCtr === undefined) block.firstVisibleCtr = elemIdCtr + if (block.lastVisibleActor !== elemIdActor || block.lastVisibleCtr !== elemIdCtr) { + block.numVisible[objectId] += 1 + } + block.lastVisibleActor = elemIdActor + block.lastVisibleCtr = elemIdCtr + } + } } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val } -Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) checkOffset(offset, byteLength, this.length) - - var i = byteLength - var mul = 1 - var val = this[offset + --i] - while (i > 0 && (mul *= 0x100)) { - val += this[offset + --i] * mul +/** + * Updates a block's metadata based on an operation being added to a block. + */ +function addBlockOperation(block, op, objectId, actorIds, isChangeOp) { + // Keep track of the largest objectId contained within a block + if (op[objActorIdx] !== null && op[objCtrIdx] !== null && + (block.lastObjectCtr === undefined || block.lastObjectCtr < op[objCtrIdx] || + (block.lastObjectCtr === op[objCtrIdx] && actorIds[block.lastObjectActor] < actorIds[op[objActorIdx]]))) { + block.lastObjectActor = op[objActorIdx] + block.lastObjectCtr = op[objCtrIdx] } - mul *= 0x80 - - if (val >= mul) val -= Math.pow(2, 8 * byteLength) - - return val -} - -Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 1, this.length) - if (!(this[offset] & 0x80)) return (this[offset]) - return ((0xff - this[offset] + 1) * -1) -} - -Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset] | (this[offset + 1] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 2, this.length) - var val = this[offset + 1] | (this[offset] << 8) - return (val & 0x8000) ? val | 0xFFFF0000 : val -} - -Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - - return (this[offset]) | - (this[offset + 1] << 8) | - (this[offset + 2] << 16) | - (this[offset + 3] << 24) -} -Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) + if (op[keyStrIdx] !== null) { + // TODO this comparison should use UTF-8 encoding, not JavaScript's UTF-16 + if (block.lastKey[objectId] === undefined || block.lastKey[objectId] < op[keyStrIdx]) { + block.lastKey[objectId] = op[keyStrIdx] + } + } else { + // List element + const elemIdActor = op[insertIdx] ? op[idActorIdx] : op[keyActorIdx] + const elemIdCtr = op[insertIdx] ? op[idCtrIdx] : op[keyCtrIdx] + bloomFilterAdd(block.bloom, elemIdActor, elemIdCtr) - return (this[offset] << 24) | - (this[offset + 1] << 16) | - (this[offset + 2] << 8) | - (this[offset + 3]) + if (op[succNumIdx] === 0 || isChangeOp) { + if (block.firstVisibleActor === undefined) block.firstVisibleActor = elemIdActor + if (block.firstVisibleCtr === undefined) block.firstVisibleCtr = elemIdCtr + block.lastVisibleActor = elemIdActor + block.lastVisibleCtr = elemIdCtr + } + } } -Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, true, 23, 4) -} +/** + * Takes a block containing too many operations, and splits it into a sequence of adjacent blocks of + * roughly equal size. + */ +function splitBlock(block, actorIds) { + for (let col of block.columns) col.decoder.reset() -Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 4, this.length) - return ieee754.read(this, offset, false, 23, 4) -} + // Make each of the resulting blocks between 50% and 80% full (leaving a bit of space in each + // block so that it doesn't get split again right away the next time an operation is added). + // The upper bound cannot be lower than 75% since otherwise we would end up with a block less than + // 50% full when going from two to three blocks. + const numBlocks = Math.ceil(block.numOps / (0.8 * MAX_BLOCK_SIZE)) + let blocks = [], opsSoFar = 0 -Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, true, 52, 8) -} + for (let i = 1; i <= numBlocks; i++) { + const opsToCopy = Math.ceil(i * block.numOps / numBlocks) - opsSoFar + const encoders = block.columns.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) + copyColumns(encoders, block.columns, opsToCopy) + const decoders = encoders.map(col => { + const decoder = decoderByColumnId(col.columnId, col.encoder.buffer) + return {columnId: col.columnId, decoder} + }) -Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { - offset = offset >>> 0 - if (!noAssert) checkOffset(offset, 8, this.length) - return ieee754.read(this, offset, false, 52, 8) -} + const newBlock = {columns: decoders} + updateBlockMetadata(newBlock, actorIds) + blocks.push(newBlock) + opsSoFar += opsToCopy + } -function checkInt (buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') - if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') - if (offset + ext > buf.length) throw new RangeError('Index out of range') + return blocks } -Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } +/** + * Takes an array of blocks and concatenates the corresponding columns across all of the blocks. + */ +function concatBlocks(blocks) { + const encoders = blocks[0].columns.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) - var mul = 1 - var i = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF + for (let block of blocks) { + for (let col of block.columns) col.decoder.reset() + copyColumns(encoders, block.columns, block.numOps) } - - return offset + byteLength + return encoders } -Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - byteLength = byteLength >>> 0 - if (!noAssert) { - var maxBytes = Math.pow(2, 8 * byteLength) - 1 - checkInt(this, value, offset, byteLength, maxBytes, 0) - } +/** + * Copies `count` rows from the set of input columns `inCols` to the set of output columns + * `outCols`. The input columns are given as an array of `{columnId, decoder}` objects, and the + * output columns are given as an array of `{columnId, encoder}` objects. Both are sorted in + * increasing order of columnId. If there is no matching input column for a given output column, it + * is filled in with `count` blank values (according to the column type). + */ +function copyColumns(outCols, inCols, count) { + if (count === 0) return + let inIndex = 0, lastGroup = -1, lastCardinality = 0, valueColumn = -1, valueBytes = 0 + for (let outCol of outCols) { + while (inIndex < inCols.length && inCols[inIndex].columnId < outCol.columnId) inIndex++ + let inCol = null + if (inIndex < inCols.length && inCols[inIndex].columnId === outCol.columnId && + inCols[inIndex].decoder.buf.byteLength > 0) { + inCol = inCols[inIndex].decoder + } + const colCount = (outCol.columnId >> 4 === lastGroup) ? lastCardinality : count - var i = byteLength - 1 - var mul = 1 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = (value / mul) & 0xFF + if (outCol.columnId % 8 === COLUMN_TYPE.GROUP_CARD) { + lastGroup = outCol.columnId >> 4 + if (inCol) { + lastCardinality = outCol.encoder.copyFrom(inCol, {count, sumValues: true}).sum + } else { + outCol.encoder.appendValue(0, count) + lastCardinality = 0 + } + } else if (outCol.columnId % 8 === COLUMN_TYPE.VALUE_LEN) { + if (inCol) { + if (inIndex + 1 === inCols.length || inCols[inIndex + 1].columnId !== outCol.columnId + 1) { + throw new RangeError('VALUE_LEN column without accompanying VALUE_RAW column') + } + valueColumn = outCol.columnId + 1 + valueBytes = outCol.encoder.copyFrom(inCol, {count: colCount, sumValues: true, sumShift: 4}).sum + } else { + outCol.encoder.appendValue(null, colCount) + valueColumn = outCol.columnId + 1 + valueBytes = 0 + } + } else if (outCol.columnId % 8 === COLUMN_TYPE.VALUE_RAW) { + if (outCol.columnId !== valueColumn) { + throw new RangeError('VALUE_RAW column without accompanying VALUE_LEN column') + } + if (valueBytes > 0) { + outCol.encoder.appendRawBytes(inCol.readRawBytes(valueBytes)) + } + } else { // ACTOR_ID, INT_RLE, INT_DELTA, BOOLEAN, or STRING_RLE + if (inCol) { + outCol.encoder.copyFrom(inCol, {count: colCount}) + } else { + const blankValue = (outCol.columnId % 8 === COLUMN_TYPE.BOOLEAN) ? false : null + outCol.encoder.appendValue(blankValue, colCount) + } + } } - - return offset + byteLength } -Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) - this[offset] = (value & 0xff) - return offset + 1 -} +/** + * Parses one operation from a set of columns. The argument `columns` contains a list of objects + * with `columnId` and `decoder` properties. Returns an array in which the i'th element is the + * value read from the i'th column in `columns`. Does not interpret datatypes; the only + * interpretation of values is that if `actorTable` is given, a value `v` in a column of type + * ACTOR_ID is replaced with `actorTable[v]`. + */ +function readOperation(columns, actorTable) { + let operation = [], colValue, lastGroup = -1, lastCardinality = 0, valueColumn = -1, valueBytes = 0 + for (let col of columns) { + if (col.columnId % 8 === COLUMN_TYPE.VALUE_RAW) { + if (col.columnId !== valueColumn) throw new RangeError('unexpected VALUE_RAW column') + colValue = col.decoder.readRawBytes(valueBytes) + } else if (col.columnId % 8 === COLUMN_TYPE.GROUP_CARD) { + lastGroup = col.columnId >> 4 + lastCardinality = col.decoder.readValue() || 0 + colValue = lastCardinality + } else if (col.columnId >> 4 === lastGroup) { + colValue = [] + if (col.columnId % 8 === COLUMN_TYPE.VALUE_LEN) { + valueColumn = col.columnId + 1 + valueBytes = 0 + } + for (let i = 0; i < lastCardinality; i++) { + let value = col.decoder.readValue() + if (col.columnId % 8 === COLUMN_TYPE.ACTOR_ID && actorTable && typeof value === 'number') { + value = actorTable[value] + } + if (col.columnId % 8 === COLUMN_TYPE.VALUE_LEN) { + valueBytes += colValue >>> 4 + } + colValue.push(value) + } + } else { + colValue = col.decoder.readValue() + if (col.columnId % 8 === COLUMN_TYPE.ACTOR_ID && actorTable && typeof colValue === 'number') { + colValue = actorTable[colValue] + } + if (col.columnId % 8 === COLUMN_TYPE.VALUE_LEN) { + valueColumn = col.columnId + 1 + valueBytes = colValue >>> 4 + } + } -Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 + operation.push(colValue) + } + return operation } -Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} +/** + * Appends `operation`, in the form returned by `readOperation()`, to the columns in `outCols`. The + * argument `inCols` provides metadata about the types of columns in `operation`; the value + * `operation[i]` comes from the column `inCols[i]`. + */ +function appendOperation(outCols, inCols, operation) { + let inIndex = 0, lastGroup = -1, lastCardinality = 0 + for (let outCol of outCols) { + while (inIndex < inCols.length && inCols[inIndex].columnId < outCol.columnId) inIndex++ -Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset + 3] = (value >>> 24) - this[offset + 2] = (value >>> 16) - this[offset + 1] = (value >>> 8) - this[offset] = (value & 0xff) - return offset + 4 + if (inIndex < inCols.length && inCols[inIndex].columnId === outCol.columnId) { + const colValue = operation[inIndex] + if (outCol.columnId % 8 === COLUMN_TYPE.GROUP_CARD) { + lastGroup = outCol.columnId >> 4 + lastCardinality = colValue + outCol.encoder.appendValue(colValue) + } else if (outCol.columnId >> 4 === lastGroup) { + if (!Array.isArray(colValue) || colValue.length !== lastCardinality) { + throw new RangeError('bad group value') + } + for (let v of colValue) outCol.encoder.appendValue(v) + } else if (outCol.columnId % 8 === COLUMN_TYPE.VALUE_RAW) { + if (colValue) outCol.encoder.appendRawBytes(colValue) + } else { + outCol.encoder.appendValue(colValue) + } + } else if (outCol.columnId % 8 === COLUMN_TYPE.GROUP_CARD) { + lastGroup = outCol.columnId >> 4 + lastCardinality = 0 + outCol.encoder.appendValue(0) + } else if (outCol.columnId % 8 !== COLUMN_TYPE.VALUE_RAW) { + const count = (outCol.columnId >> 4 === lastGroup) ? lastCardinality : 1 + let blankValue = null + if (outCol.columnId % 8 === COLUMN_TYPE.BOOLEAN) blankValue = false + if (outCol.columnId % 8 === COLUMN_TYPE.VALUE_LEN) blankValue = 0 + outCol.encoder.appendValue(blankValue, count) + } + } } -Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 +/** + * Parses the next operation from block `blockIndex` of the document. Returns an object of the form + * `{docOp, blockIndex}` where `docOp` is an operation in the form returned by `readOperation()`, + * and `blockIndex` is the block number to use on the next call (it moves on to the next block when + * we reach the end of the current block). `docOp` is null if there are no more operations. + */ +function readNextDocOp(docState, blockIndex) { + let block = docState.blocks[blockIndex] + if (!block.columns[actionIdx].decoder.done) { + return {docOp: readOperation(block.columns), blockIndex} + } else if (blockIndex === docState.blocks.length - 1) { + return {docOp: null, blockIndex} + } else { + blockIndex += 1 + block = docState.blocks[blockIndex] + for (let col of block.columns) col.decoder.reset() + return {docOp: readOperation(block.columns), blockIndex} + } } -Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) +/** + * Parses the next operation from a sequence of changes. `changeState` serves as the state of this + * pseudo-iterator, and it is mutated to reflect the new operation. In particular, + * `changeState.nextOp` is set to the operation that was read, and `changeState.done` is set to true + * when we have finished reading the last operation in the last change. + */ +function readNextChangeOp(docState, changeState) { + // If we've finished reading one change, move to the next change that contains at least one op + while (changeState.changeIndex < changeState.changes.length - 1 && + (!changeState.columns || changeState.columns[actionIdx].decoder.done)) { + changeState.changeIndex += 1 + const change = changeState.changes[changeState.changeIndex] + changeState.columns = makeDecoders(change.columns, CHANGE_COLUMNS) + changeState.opCtr = change.startOp - checkInt(this, value, offset, byteLength, limit - 1, -limit) + // Update docState based on the information in the change + updateBlockColumns(docState, changeState.columns) + const {actorIds, actorTable} = getActorTable(docState.actorIds, change) + docState.actorIds = actorIds + changeState.actorTable = actorTable + changeState.actorIndex = docState.actorIds.indexOf(change.actorIds[0]) } - var i = 0 - var mul = 1 - var sub = 0 - this[offset] = value & 0xFF - while (++i < byteLength && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + // Reached the end of the last change? + if (changeState.columns[actionIdx].decoder.done) { + changeState.done = true + changeState.nextOp = null + return } - return offset + byteLength -} - -Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - var limit = Math.pow(2, (8 * byteLength) - 1) + changeState.nextOp = readOperation(changeState.columns, changeState.actorTable) + changeState.nextOp[idActorIdx] = changeState.actorIndex + changeState.nextOp[idCtrIdx] = changeState.opCtr + changeState.changes[changeState.changeIndex].maxOp = changeState.opCtr + if (changeState.opCtr > docState.maxOp) docState.maxOp = changeState.opCtr + changeState.opCtr += 1 - checkInt(this, value, offset, byteLength, limit - 1, -limit) + const op = changeState.nextOp + if ((op[objCtrIdx] === null && op[objActorIdx] !== null) || + (op[objCtrIdx] !== null && op[objActorIdx] === null)) { + throw new RangeError(`Mismatched object reference: (${op[objCtrIdx]}, ${op[objActorIdx]})`) } - - var i = byteLength - 1 - var mul = 1 - var sub = 0 - this[offset + i] = value & 0xFF - while (--i >= 0 && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { - sub = 1 - } - this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + if ((op[keyCtrIdx] === null && op[keyActorIdx] !== null) || + (op[keyCtrIdx] === 0 && op[keyActorIdx] !== null) || + (op[keyCtrIdx] > 0 && op[keyActorIdx] === null)) { + throw new RangeError(`Mismatched operation key: (${op[keyCtrIdx]}, ${op[keyActorIdx]})`) } - - return offset + byteLength } -Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) - if (value < 0) value = 0xff + value + 1 - this[offset] = (value & 0xff) - return offset + 1 +function emptyObjectPatch(objectId, type) { + if (type === 'list' || type === 'text') { + return {objectId, type, edits: []} + } else { + return {objectId, type, props: {}} + } } -Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - return offset + 2 +/** + * Returns true if the two given operation IDs have the same actor ID, and the counter of `id2` is + * exactly `delta` greater than the counter of `id1`. + */ +function opIdDelta(id1, id2, delta = 1) { + const parsed1 = parseOpId(id1), parsed2 = parseOpId(id2) + return parsed1.actorId === parsed2.actorId && parsed1.counter + delta === parsed2.counter } -Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) - this[offset] = (value >>> 8) - this[offset + 1] = (value & 0xff) - return offset + 2 -} +/** + * Appends a list edit operation (insert, update, remove) to an array of existing operations. If the + * last existing operation can be extended (as a multi-op), we do that. + */ +function appendEdit(existingEdits, nextEdit) { + if (existingEdits.length === 0) { + existingEdits.push(nextEdit) + return + } -Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - this[offset] = (value & 0xff) - this[offset + 1] = (value >>> 8) - this[offset + 2] = (value >>> 16) - this[offset + 3] = (value >>> 24) - return offset + 4 -} + let lastEdit = existingEdits[existingEdits.length - 1] + if (lastEdit.action === 'insert' && nextEdit.action === 'insert' && + lastEdit.index === nextEdit.index - 1 && + lastEdit.value.type === 'value' && nextEdit.value.type === 'value' && + lastEdit.elemId === lastEdit.opId && nextEdit.elemId === nextEdit.opId && + opIdDelta(lastEdit.elemId, nextEdit.elemId, 1) && + lastEdit.value.datatype === nextEdit.value.datatype && + typeof lastEdit.value.value === typeof nextEdit.value.value) { + lastEdit.action = 'multi-insert' + if (nextEdit.value.datatype) lastEdit.datatype = nextEdit.value.datatype + lastEdit.values = [lastEdit.value.value, nextEdit.value.value] + delete lastEdit.value + delete lastEdit.opId -Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) - if (value < 0) value = 0xffffffff + value + 1 - this[offset] = (value >>> 24) - this[offset + 1] = (value >>> 16) - this[offset + 2] = (value >>> 8) - this[offset + 3] = (value & 0xff) - return offset + 4 + } else if (lastEdit.action === 'multi-insert' && nextEdit.action === 'insert' && + lastEdit.index + lastEdit.values.length === nextEdit.index && + nextEdit.value.type === 'value' && nextEdit.elemId === nextEdit.opId && + opIdDelta(lastEdit.elemId, nextEdit.elemId, lastEdit.values.length) && + lastEdit.datatype === nextEdit.value.datatype && + typeof lastEdit.values[0] === typeof nextEdit.value.value) { + lastEdit.values.push(nextEdit.value.value) + + } else if (lastEdit.action === 'remove' && nextEdit.action === 'remove' && + lastEdit.index === nextEdit.index) { + lastEdit.count += nextEdit.count + + } else { + existingEdits.push(nextEdit) + } } -function checkIEEE754 (buf, value, offset, ext, max, min) { - if (offset + ext > buf.length) throw new RangeError('Index out of range') - if (offset < 0) throw new RangeError('Index out of range') +/** + * `edits` is an array of (SingleInsertEdit | MultiInsertEdit | UpdateEdit | RemoveEdit) list edits + * for a patch. This function appends an UpdateEdit to this array. A conflict is represented by + * having several consecutive edits with the same index, and this can be realised by calling + * `appendUpdate` several times for the same list element. On the first such call, `firstUpdate` + * must be true. + * + * It is possible that coincidentally the previous edit (potentially arising from a different + * change) is for the same index. If this is the case, to avoid accidentally treating consecutive + * updates for the same index as a conflict, we remove the previous edit for the same index. This is + * safe because the previous edit is overwritten by the new edit being appended, and we know that + * it's for the same list elements because there are no intervening insertions/deletions that could + * have changed the indexes. + */ +function appendUpdate(edits, index, elemId, opId, value, firstUpdate) { + let insert = false + if (firstUpdate) { + // Pop all edits for the same index off the end of the edits array. This sequence may begin with + // either an insert or an update. If it's an insert, we remember that fact, and use it below. + while (!insert && edits.length > 0) { + const lastEdit = edits[edits.length - 1] + if ((lastEdit.action === 'insert' || lastEdit.action === 'update') && lastEdit.index === index) { + edits.pop() + insert = (lastEdit.action === 'insert') + } else if (lastEdit.action === 'multi-insert' && lastEdit.index + lastEdit.values.length - 1 === index) { + lastEdit.values.pop() + insert = true + } else { + break + } + } + } + + // If we popped an insert edit off the edits array, we need to turn the new update into an insert + // in order to ensure the list element still gets inserted (just with a new value). + if (insert) { + appendEdit(edits, {action: 'insert', index, elemId, opId, value}) + } else { + appendEdit(edits, {action: 'update', index, opId, value}) + } } -function writeFloat (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) +/** + * `edits` is an array of (SingleInsertEdit | MultiInsertEdit | UpdateEdit | RemoveEdit) list edits + * for a patch. We assume that there is a suffix of this array that consists of an insertion at + * position `index`, followed by zero or more UpdateEdits at the same index. This function rewrites + * that suffix to be all updates instead. This is needed because sometimes when generating a patch + * we think we are performing a list insertion, but then it later turns out that there was already + * an existing value at that list element, and so we actually need to do an update, not an insert. + * + * If the suffix is preceded by one or more updates at the same index, those earlier updates are + * removed by `appendUpdate()` to ensure we don't inadvertently treat them as part of the same + * conflict. + */ +function convertInsertToUpdate(edits, index, elemId) { + let updates = [] + while (edits.length > 0) { + let lastEdit = edits[edits.length - 1] + if (lastEdit.action === 'insert') { + if (lastEdit.index !== index) throw new RangeError('last edit has unexpected index') + updates.unshift(edits.pop()) + break + } else if (lastEdit.action === 'update') { + if (lastEdit.index !== index) throw new RangeError('last edit has unexpected index') + updates.unshift(edits.pop()) + } else { + // It's impossible to encounter a remove edit here because the state machine in + // updatePatchProperty() ensures that a property can have either an insert or a remove edit, + // but not both. It's impossible to encounter a multi-insert here because multi-inserts always + // have equal elemId and opId (i.e. they can only be used for the operation that first inserts + // an element, but not for any subsequent assignments to that list element); moreover, + // convertInsertToUpdate is only called if an insert action is followed by a non-overwritten + // document op. The fact that there is a non-overwritten document op after another op on the + // same list element implies that the original insertion op for that list element must be + // overwritten, and thus the original insertion op cannot have given rise to a multi-insert. + throw new RangeError('last edit has unexpected action') + } } - ieee754.write(buf, value, offset, littleEndian, 23, 4) - return offset + 4 -} -Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { - return writeFloat(this, value, offset, true, noAssert) + // Now take the edits we popped off and push them back onto the list again + let firstUpdate = true + for (let update of updates) { + appendUpdate(edits, index, elemId, update.opId, update.value, firstUpdate) + firstUpdate = false + } } -Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { - return writeFloat(this, value, offset, false, noAssert) -} +/** + * Updates `patches` to reflect the operation `op` within the document with state `docState`. + * Can be called multiple times if there are multiple operations for the same property (e.g. due + * to a conflict). `propState` is an object that carries over state between such successive + * invocations for the same property. If the current object is a list, `listIndex` is the index + * into that list (counting only visible elements). If the operation `op` was already previously + * in the document, `oldSuccNum` is the value of `op[succNumIdx]` before the current change was + * applied (allowing us to determine whether this operation was overwritten or deleted in the + * current change). `oldSuccNum` must be undefined if the operation came from the current change. + * If we are creating an incremental patch as a result of applying one or more changes, `newBlock` + * is the block to which the operations are getting written; we will update the metadata on this + * block. `newBlock` should be null if we are creating a patch for the whole document. + */ +function updatePatchProperty(patches, newBlock, objectId, op, docState, propState, listIndex, oldSuccNum) { + const isWholeDoc = !newBlock + const type = op[actionIdx] < ACTIONS.length ? OBJECT_TYPE[ACTIONS[op[actionIdx]]] : null + const opId = `${op[idCtrIdx]}@${docState.actorIds[op[idActorIdx]]}` + const elemIdActor = op[insertIdx] ? op[idActorIdx] : op[keyActorIdx] + const elemIdCtr = op[insertIdx] ? op[idCtrIdx] : op[keyCtrIdx] + const elemId = op[keyStrIdx] ? op[keyStrIdx] : `${elemIdCtr}@${docState.actorIds[elemIdActor]}` -function writeDouble (buf, value, offset, littleEndian, noAssert) { - value = +value - offset = offset >>> 0 - if (!noAssert) { - checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + // When the change contains a new make* operation (i.e. with an even-numbered action), record the + // new parent-child relationship in objectMeta. TODO: also handle link/move operations. + if (op[actionIdx] % 2 === 0 && !docState.objectMeta[opId]) { + docState.objectMeta[opId] = {parentObj: objectId, parentKey: elemId, opId, type, children: {}} + deepCopyUpdate(docState.objectMeta, [objectId, 'children', elemId, opId], {objectId: opId, type, props: {}}) } - ieee754.write(buf, value, offset, littleEndian, 52, 8) - return offset + 8 -} -Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { - return writeDouble(this, value, offset, true, noAssert) -} + // firstOp is true if the current operation is the first of a sequence of ops for the same key + const firstOp = !propState[elemId] + if (!propState[elemId]) propState[elemId] = {visibleOps: [], hasChild: false} -Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { - return writeDouble(this, value, offset, false, noAssert) -} + // An operation is overwritten if it is a document operation that has at least one successor + const isOverwritten = (oldSuccNum !== undefined && op[succNumIdx] > 0) -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function copy (target, targetStart, start, end) { - if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') - if (!start) start = 0 - if (!end && end !== 0) end = this.length - if (targetStart >= target.length) targetStart = target.length - if (!targetStart) targetStart = 0 - if (end > 0 && end < start) end = start + // Record all visible values for the property, and whether it has any child object + if (!isOverwritten) { + propState[elemId].visibleOps.push(op) + propState[elemId].hasChild = propState[elemId].hasChild || (op[actionIdx] % 2) === 0 // even-numbered action == make* operation + } - // Copy 0 bytes; we're done - if (end === start) return 0 - if (target.length === 0 || this.length === 0) return 0 + // If one or more of the values of the property is a child object, we update objectMeta to store + // all of the visible values of the property (even the non-child-object values). Then, when we + // subsequently process an update within that child object, we can construct the patch to + // contain the conflicting values. + const prevChildren = docState.objectMeta[objectId].children[elemId] + if (propState[elemId].hasChild || (prevChildren && Object.keys(prevChildren).length > 0)) { + let values = {} + for (let visible of propState[elemId].visibleOps) { + const opId = `${visible[idCtrIdx]}@${docState.actorIds[visible[idActorIdx]]}` + if (ACTIONS[visible[actionIdx]] === 'set') { + values[opId] = Object.assign({type: 'value'}, decodeValue(visible[valLenIdx], visible[valRawIdx])) + } else if (visible[actionIdx] % 2 === 0) { + const objType = visible[actionIdx] < ACTIONS.length ? OBJECT_TYPE[ACTIONS[visible[actionIdx]]] : null + values[opId] = emptyObjectPatch(opId, objType) + } + } - // Fatal error conditions - if (targetStart < 0) { - throw new RangeError('targetStart out of bounds') + // Copy so that objectMeta is not modified if an exception is thrown while applying change + deepCopyUpdate(docState.objectMeta, [objectId, 'children', elemId], values) } - if (start < 0 || start >= this.length) throw new RangeError('Index out of range') - if (end < 0) throw new RangeError('sourceEnd out of bounds') - // Are we oob? - if (end > this.length) end = this.length - if (target.length - targetStart < end - start) { - end = target.length - targetStart + start - } + let patchKey, patchValue - var len = end - start + // For counters, increment operations are succs to the set operation that created the counter, + // but in this case we want to add the values rather than overwriting them. + if (isOverwritten && ACTIONS[op[actionIdx]] === 'set' && (op[valLenIdx] & 0x0f) === VALUE_TYPE.COUNTER) { + // This is the initial set operation that creates a counter. Initialise the counter state + // to contain all successors of the set operation. Only if we later find that each of these + // successor operations is an increment, we make the counter visible in the patch. + if (!propState[elemId]) propState[elemId] = {visibleOps: [], hasChild: false} + if (!propState[elemId].counterStates) propState[elemId].counterStates = {} + let counterStates = propState[elemId].counterStates + let counterState = {opId, value: decodeValue(op[valLenIdx], op[valRawIdx]).value, succs: {}} - if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { - // Use built-in when available, missing from IE11 - this.copyWithin(targetStart, start, end) - } else if (this === target && start < targetStart && targetStart < end) { - // descending copy from end - for (var i = len - 1; i >= 0; --i) { - target[i + targetStart] = this[i + start] + for (let i = 0; i < op[succNumIdx]; i++) { + const succOp = `${op[succCtrIdx][i]}@${docState.actorIds[op[succActorIdx][i]]}` + counterStates[succOp] = counterState + counterState.succs[succOp] = true } - } else { - Uint8Array.prototype.set.call( - target, - this.subarray(start, end), - targetStart - ) - } - return len -} + } else if (ACTIONS[op[actionIdx]] === 'inc') { + // Incrementing a previously created counter. + if (!propState[elemId] || !propState[elemId].counterStates || !propState[elemId].counterStates[opId]) { + throw new RangeError(`increment operation ${opId} for unknown counter`) + } + let counterState = propState[elemId].counterStates[opId] + counterState.value += decodeValue(op[valLenIdx], op[valRawIdx]).value + delete counterState.succs[opId] -// Usage: -// buffer.fill(number[, offset[, end]]) -// buffer.fill(buffer[, offset[, end]]) -// buffer.fill(string[, offset[, end]][, encoding]) -Buffer.prototype.fill = function fill (val, start, end, encoding) { - // Handle string cases: - if (typeof val === 'string') { - if (typeof start === 'string') { - encoding = start - start = 0 - end = this.length - } else if (typeof end === 'string') { - encoding = end - end = this.length + if (Object.keys(counterState.succs).length === 0) { + patchKey = counterState.opId + patchValue = {type: 'value', datatype: 'counter', value: counterState.value} + // TODO if the counter is in a list element, we need to add a 'remove' action when deleted } - if (encoding !== undefined && typeof encoding !== 'string') { - throw new TypeError('encoding must be a string') + + } else if (!isOverwritten) { + // Add the value to the patch if it is not overwritten (i.e. if it has no succs). + if (ACTIONS[op[actionIdx]] === 'set') { + patchKey = opId + patchValue = Object.assign({type: 'value'}, decodeValue(op[valLenIdx], op[valRawIdx])) + } else if (op[actionIdx] % 2 === 0) { // even-numbered action == make* operation + if (!patches[opId]) patches[opId] = emptyObjectPatch(opId, type) + patchKey = opId + patchValue = patches[opId] } - if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding) + } + + if (!patches[objectId]) patches[objectId] = emptyObjectPatch(objectId, docState.objectMeta[objectId].type) + const patch = patches[objectId] + + // Updating a list or text object (with elemId key) + if (op[keyStrIdx] === null) { + // If we come across any document op that was previously non-overwritten/non-deleted, that + // means the current list element already had a value before this change was applied, and + // therefore the current element cannot be an insert. If we already registered an insert, we + // have to convert it into an update. + if (oldSuccNum === 0 && !isWholeDoc && propState[elemId].action === 'insert') { + propState[elemId].action = 'update' + convertInsertToUpdate(patch.edits, listIndex, elemId) + if (newBlock) newBlock.numVisible[objectId] -= 1 } - if (val.length === 1) { - var code = val.charCodeAt(0) - if ((encoding === 'utf8' && code < 128) || - encoding === 'latin1') { - // Fast path: If `val` fits into a single byte, use that numeric value. - val = code + + if (patchValue) { + // If the op has a non-overwritten value and it came from the change, it's an insert. + // (It's not necessarily the case that op[insertIdx] is true: if a list element is concurrently + // deleted and updated, the node that first processes the deletion and then the update will + // observe the update as a re-insertion of the deleted list element.) + if (!propState[elemId].action && (oldSuccNum === undefined || isWholeDoc)) { + propState[elemId].action = 'insert' + appendEdit(patch.edits, {action: 'insert', index: listIndex, elemId, opId: patchKey, value: patchValue}) + if (newBlock) { + if (newBlock.numVisible[objectId] === undefined) newBlock.numVisible[objectId] = 0 + newBlock.numVisible[objectId] += 1 + } + + // If the property has a value and it's not an insert, then it must be an update. + // We might have previously registered it as a remove, in which case we convert it to update. + } else if (propState[elemId].action === 'remove') { + let lastEdit = patch.edits[patch.edits.length - 1] + if (lastEdit.action !== 'remove') throw new RangeError('last edit has unexpected type') + if (lastEdit.count > 1) lastEdit.count -= 1; else patch.edits.pop() + propState[elemId].action = 'update' + appendUpdate(patch.edits, listIndex, elemId, patchKey, patchValue, true) + if (newBlock) newBlock.numVisible[objectId] += 1 + + } else { + // A 'normal' update + appendUpdate(patch.edits, listIndex, elemId, patchKey, patchValue, !propState[elemId].action) + if (!propState[elemId].action) propState[elemId].action = 'update' } + + } else if (oldSuccNum === 0 && !propState[elemId].action) { + // If the property used to have a non-overwritten/non-deleted value, but no longer, it's a remove + propState[elemId].action = 'remove' + appendEdit(patch.edits, {action: 'remove', index: listIndex, count: 1}) + if (newBlock) newBlock.numVisible[objectId] -= 1 } - } else if (typeof val === 'number') { - val = val & 255 - } - // Invalid ranges are not set to a default, so can range check early. - if (start < 0 || this.length < start || this.length < end) { - throw new RangeError('Out of range index') + } else if (patchValue || !isWholeDoc) { + // Updating a map or table (with string key) + if (firstOp || !patch.props[op[keyStrIdx]]) patch.props[op[keyStrIdx]] = {} + if (patchValue) patch.props[op[keyStrIdx]][patchKey] = patchValue } +} - if (end <= start) { - return this - } +/** + * Applies operations (from one or more changes) to the document by merging the sequence of change + * ops into the sequence of document ops. The two inputs are `changeState` and `docState` + * respectively. Assumes that the decoders of both sets of columns are at the position where we want + * to start merging. `patches` is mutated to reflect the effect of the change operations. `ops` is + * the operation sequence to apply (as decoded by `groupRelatedOps()`). `docState` is as + * documented in `applyOps()`. If the operations are updating a list or text object, `listIndex` + * is the number of visible elements that precede the position at which we start merging. + * `blockIndex` is the document block number from which we are currently reading. + */ +function mergeDocChangeOps(patches, newBlock, outCols, changeState, docState, listIndex, blockIndex) { + const firstOp = changeState.nextOp, insert = firstOp[insertIdx] + const objActor = firstOp[objActorIdx], objCtr = firstOp[objCtrIdx] + const objectId = objActor === null ? '_root' : `${objCtr}@${docState.actorIds[objActor]}` + const idActorIndex = changeState.actorIndex, idActor = docState.actorIds[idActorIndex] + let foundListElem = false, elemVisible = false, propState = {}, docOp + ;({ docOp, blockIndex } = readNextDocOp(docState, blockIndex)) + let docOpsConsumed = (docOp === null ? 0 : 1) + let docOpOldSuccNum = (docOp === null ? 0 : docOp[succNumIdx]) + let changeOp = null, changeOps = [], changeCols = [], predSeen = [], lastChangeKey = null + changeState.objectIds.add(objectId) - start = start >>> 0 - end = end === undefined ? this.length : end >>> 0 + // Merge the two inputs: the sequence of ops in the doc, and the sequence of ops in the change. + // At each iteration, we either output the doc's op (possibly updated based on the change's ops) + // or output an op from the change. + while (true) { + // The array `changeOps` contains operations from the change(s) we're applying. When the array + // is empty, we load changes from the change. Typically we load only a single operation at a + // time, with two exceptions: 1. all operations that update the same key or list element in the + // same object are put into changeOps at the same time (this is needed so that we can update the + // succ columns of the document ops correctly); 2. a run of consecutive insertions is also + // placed into changeOps in one go. + // + // When we have processed all the ops in changeOps we try to see whether there are further + // operations that we can also process while we're at it. Those operations must be for the same + // object, they must be for a key or list element that appears later in the document, they must + // either all be insertions or all be non-insertions, and if insertions, they must be + // consecutive. If these conditions are satisfied, that means the operations can be processed in + // the same pass. If we encounter an operation that does not meet these conditions, we leave + // changeOps empty, and this function returns after having processed any remaining document ops. + // + // Any operations that could not be processed in a single pass remain in changeState; applyOps + // will seek to the appropriate position and then call mergeDocChangeOps again. + if (changeOps.length === 0) { + foundListElem = false - if (!val) val = 0 + let nextOp = changeState.nextOp + while (!changeState.done && nextOp[idActorIdx] === idActorIndex && nextOp[insertIdx] === insert && + nextOp[objActorIdx] === firstOp[objActorIdx] && nextOp[objCtrIdx] === firstOp[objCtrIdx]) { - var i - if (typeof val === 'number') { - for (i = start; i < end; ++i) { - this[i] = val - } - } else { - var bytes = Buffer.isBuffer(val) - ? val - : Buffer.from(val, encoding) - var len = bytes.length - if (len === 0) { - throw new TypeError('The value "' + val + - '" is invalid for argument "value"') - } - for (i = 0; i < end - start; ++i) { - this[i + start] = bytes[i % len] + // Check if the operation's pred references a previous operation in changeOps + const lastOp = (changeOps.length > 0) ? changeOps[changeOps.length - 1] : null + let isOverwrite = false + for (let i = 0; i < nextOp[predNumIdx]; i++) { + for (let prevOp of changeOps) { + if (nextOp[predActorIdx][i] === prevOp[idActorIdx] && nextOp[predCtrIdx][i] === prevOp[idCtrIdx]) { + isOverwrite = true + } + } + } + + // If any of the following `if` statements is true, we add `nextOp` to `changeOps`. If they + // are all false, we break out of the loop and stop adding to `changeOps`. + if (nextOp === firstOp) { + // First change operation in a mergeDocChangeOps call is always used + } else if (insert && lastOp !== null && nextOp[keyStrIdx] === null && + nextOp[keyActorIdx] === lastOp[idActorIdx] && + nextOp[keyCtrIdx] === lastOp[idCtrIdx]) { + // Collect consecutive insertions + } else if (!insert && lastOp !== null && nextOp[keyStrIdx] !== null && + nextOp[keyStrIdx] === lastOp[keyStrIdx] && !isOverwrite) { + // Collect several updates to the same key + } else if (!insert && lastOp !== null && + nextOp[keyStrIdx] === null && lastOp[keyStrIdx] === null && + nextOp[keyActorIdx] === lastOp[keyActorIdx] && + nextOp[keyCtrIdx] === lastOp[keyCtrIdx] && !isOverwrite) { + // Collect several updates to the same list element + } else if (!insert && lastOp === null && nextOp[keyStrIdx] === null && + docOp && docOp[insertIdx] && docOp[keyStrIdx] === null && + docOp[idActorIdx] === nextOp[keyActorIdx] && + docOp[idCtrIdx] === nextOp[keyCtrIdx]) { + // When updating/deleting list elements, keep going if the next elemId in the change + // equals the next elemId in the doc (i.e. we're updating several consecutive elements) + } else if (!insert && lastOp === null && nextOp[keyStrIdx] !== null && + lastChangeKey !== null && lastChangeKey < nextOp[keyStrIdx]) { + // Allow a single mergeDocChangeOps call to process changes to several keys in the same + // object, provided that they appear in ascending order + } else break + + lastChangeKey = (nextOp !== null) ? nextOp[keyStrIdx] : null + changeOps.push(changeState.nextOp) + changeCols.push(changeState.columns) + predSeen.push(new Array(changeState.nextOp[predNumIdx])) + readNextChangeOp(docState, changeState) + nextOp = changeState.nextOp + } } - } - return this -} + if (changeOps.length > 0) changeOp = changeOps[0] + const inCorrectObject = docOp && docOp[objActorIdx] === changeOp[objActorIdx] && docOp[objCtrIdx] === changeOp[objCtrIdx] + const keyMatches = docOp && docOp[keyStrIdx] !== null && docOp[keyStrIdx] === changeOp[keyStrIdx] + const listElemMatches = docOp && docOp[keyStrIdx] === null && changeOp[keyStrIdx] === null && + ((!docOp[insertIdx] && docOp[keyActorIdx] === changeOp[keyActorIdx] && docOp[keyCtrIdx] === changeOp[keyCtrIdx]) || + (docOp[insertIdx] && docOp[idActorIdx] === changeOp[keyActorIdx] && docOp[idCtrIdx] === changeOp[keyCtrIdx])) -// HELPER FUNCTIONS -// ================ + // We keep going until we run out of ops in the change, except that even when we run out, we + // keep going until we have processed all doc ops for the current key/list element. + if (changeOps.length === 0 && !(inCorrectObject && (keyMatches || listElemMatches))) break -var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g + let takeDocOp = false, takeChangeOps = 0 -function base64clean (str) { - // Node takes equal signs as end of the Base64 encoding - str = str.split('=')[0] - // Node strips out invalid characters like \n and \t from the string, base64-js does not - str = str.trim().replace(INVALID_BASE64_RE, '') - // Node converts strings with length < 2 to '' - if (str.length < 2) return '' - // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not - while (str.length % 4 !== 0) { - str = str + '=' - } - return str -} + // The change operations come first if we are inserting list elements (seekToOp already + // determines the correct insertion position), if there is no document operation, if the next + // document operation is for a different object, or if the change op's string key is + // lexicographically first (TODO check ordering of keys beyond the basic multilingual plane). + if (insert || !inCorrectObject || + (docOp[keyStrIdx] === null && changeOp[keyStrIdx] !== null) || + (docOp[keyStrIdx] !== null && changeOp[keyStrIdx] !== null && changeOp[keyStrIdx] < docOp[keyStrIdx])) { + // Take the operations from the change + takeChangeOps = changeOps.length + if (!inCorrectObject && !foundListElem && changeOp[keyStrIdx] === null && !changeOp[insertIdx]) { + // This can happen if we first update one list element, then another one earlier in the + // list. That is not allowed: list element updates must occur in ascending order. + throw new RangeError("could not find list element with ID: " + + `${changeOp[keyCtrIdx]}@${docState.actorIds[changeOp[keyActorIdx]]}`) + } -function toHex (n) { - if (n < 16) return '0' + n.toString(16) - return n.toString(16) -} + } else if (keyMatches || listElemMatches || foundListElem) { + // The doc operation is for the same key or list element in the same object as the change + // ops, so we merge them. First, if any of the change ops' `pred` matches the opId of the + // document operation, we update the document operation's `succ` accordingly. + for (let opIndex = 0; opIndex < changeOps.length; opIndex++) { + const op = changeOps[opIndex] + for (let i = 0; i < op[predNumIdx]; i++) { + if (op[predActorIdx][i] === docOp[idActorIdx] && op[predCtrIdx][i] === docOp[idCtrIdx]) { + // Insert into the doc op's succ list such that the lists remains sorted + let j = 0 + while (j < docOp[succNumIdx] && (docOp[succCtrIdx][j] < op[idCtrIdx] || + docOp[succCtrIdx][j] === op[idCtrIdx] && docState.actorIds[docOp[succActorIdx][j]] < idActor)) j++ + docOp[succCtrIdx].splice(j, 0, op[idCtrIdx]) + docOp[succActorIdx].splice(j, 0, idActorIndex) + docOp[succNumIdx]++ + predSeen[opIndex][i] = true + break + } + } + } -function utf8ToBytes (string, units) { - units = units || Infinity - var codePoint - var length = string.length - var leadSurrogate = null - var bytes = [] + if (listElemMatches) foundListElem = true - for (var i = 0; i < length; ++i) { - codePoint = string.charCodeAt(i) + if (foundListElem && !listElemMatches) { + // If the previous docOp was for the correct list element, and the current docOp is for + // the wrong list element, then place the current changeOp before the docOp. + takeChangeOps = changeOps.length - // is surrogate component - if (codePoint > 0xD7FF && codePoint < 0xE000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xDBFF) { - // unexpected trail - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue - } else if (i + 1 === length) { - // unpaired lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - continue + } else if (changeOps.length === 0 || docOp[idCtrIdx] < changeOp[idCtrIdx] || + (docOp[idCtrIdx] === changeOp[idCtrIdx] && docState.actorIds[docOp[idActorIdx]] < idActor)) { + // When we have several operations for the same object and the same key, we want to keep + // them sorted in ascending order by opId. Here we have docOp with a lower opId, so we + // output it first. + takeDocOp = true + updatePatchProperty(patches, newBlock, objectId, docOp, docState, propState, listIndex, docOpOldSuccNum) + + // A deletion op in the change is represented in the document only by its entries in the + // succ list of the operations it overwrites; it has no separate row in the set of ops. + for (let i = changeOps.length - 1; i >= 0; i--) { + let deleted = true + for (let j = 0; j < changeOps[i][predNumIdx]; j++) { + if (!predSeen[i][j]) deleted = false + } + if (ACTIONS[changeOps[i][actionIdx]] === 'del' && deleted) { + changeOps.splice(i, 1) + changeCols.splice(i, 1) + predSeen.splice(i, 1) + } } - // valid lead - leadSurrogate = codePoint + } else if (docOp[idCtrIdx] === changeOp[idCtrIdx] && docState.actorIds[docOp[idActorIdx]] === idActor) { + throw new RangeError(`duplicate operation ID: ${changeOp[idCtrIdx]}@${idActor}`) + } else { + // The changeOp has the lower opId, so we output it first. + takeChangeOps = 1 + } + } else { + // The document operation comes first if its string key is lexicographically first, or if + // we're using opId keys and the keys don't match (i.e. we scan the document until we find a + // matching key). + takeDocOp = true + } + + if (takeDocOp) { + appendOperation(outCols, docState.blocks[blockIndex].columns, docOp) + addBlockOperation(newBlock, docOp, objectId, docState.actorIds, false) + + if (docOp[insertIdx] && elemVisible) { + elemVisible = false + listIndex++ + } + if (docOp[succNumIdx] === 0) elemVisible = true + newBlock.numOps++ + ;({ docOp, blockIndex } = readNextDocOp(docState, blockIndex)) + if (docOp !== null) { + docOpsConsumed++ + docOpOldSuccNum = docOp[succNumIdx] + } + } + + if (takeChangeOps > 0) { + for (let i = 0; i < takeChangeOps; i++) { + let op = changeOps[i] + // Check that we've seen all ops mentioned in `pred` (they must all have lower opIds than + // the change op's own opId, so we must have seen them already) + for (let j = 0; j < op[predNumIdx]; j++) { + if (!predSeen[i][j]) { + throw new RangeError(`no matching operation for pred: ${op[predCtrIdx][j]}@${docState.actorIds[op[predActorIdx][j]]}`) + } + } + updatePatchProperty(patches, newBlock, objectId, op, docState, propState, listIndex) + appendOperation(outCols, changeCols[i], op) + addBlockOperation(newBlock, op, objectId, docState.actorIds, true) - continue + if (op[insertIdx]) { + elemVisible = false + listIndex++ + } else { + elemVisible = true + } } - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = codePoint - continue + if (takeChangeOps === changeOps.length) { + changeOps.length = 0 + changeCols.length = 0 + predSeen.length = 0 + } else { + changeOps.splice(0, takeChangeOps) + changeCols.splice(0, takeChangeOps) + predSeen.splice(0, takeChangeOps) } - - // valid surrogate pair - codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - } - - leadSurrogate = null - - // encode utf8 - if (codePoint < 0x80) { - if ((units -= 1) < 0) break - bytes.push(codePoint) - } else if (codePoint < 0x800) { - if ((units -= 2) < 0) break - bytes.push( - codePoint >> 0x6 | 0xC0, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x10000) { - if ((units -= 3) < 0) break - bytes.push( - codePoint >> 0xC | 0xE0, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else if (codePoint < 0x110000) { - if ((units -= 4) < 0) break - bytes.push( - codePoint >> 0x12 | 0xF0, - codePoint >> 0xC & 0x3F | 0x80, - codePoint >> 0x6 & 0x3F | 0x80, - codePoint & 0x3F | 0x80 - ) - } else { - throw new Error('Invalid code point') + newBlock.numOps += takeChangeOps } } - return bytes -} - -function asciiToBytes (str) { - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push(str.charCodeAt(i) & 0xFF) + if (docOp) { + appendOperation(outCols, docState.blocks[blockIndex].columns, docOp) + newBlock.numOps++ + if (docOp[objActorIdx] === objActor && docOp[objCtrIdx] === objCtr) { + addBlockOperation(newBlock, docOp, objectId, docState.actorIds, false) + } } - return byteArray + return {docOpsConsumed, blockIndex} } -function utf16leToBytes (str, units) { - var c, hi, lo - var byteArray = [] - for (var i = 0; i < str.length; ++i) { - if ((units -= 2) < 0) break - - c = str.charCodeAt(i) - hi = c >> 8 - lo = c % 256 - byteArray.push(lo) - byteArray.push(hi) +/** + * Applies operations from the change (or series of changes) in `changeState` to the document + * `docState`. Passing `changeState` to `readNextChangeOp` allows iterating over the change ops. + * `docState` is an object with keys: + * - `actorIds` is an array of actorIds (as hex strings) occurring in the document (values in + * the document's objActor/keyActor/idActor/... columns are indexes into this array). + * - `blocks` is an array of all the blocks of operations in the document. + * - `objectMeta` is a map from objectId to metadata about that object. + * + * `docState` is mutated to contain the updated document state. + * `patches` is a patch object that is mutated to reflect the operations applied by this function. + */ +function applyOps(patches, changeState, docState) { + const [objActorNum, objCtr, keyActorNum, keyCtr, keyStr, idActorNum, idCtr, insert] = changeState.nextOp + const objActor = objActorNum === null ? null : docState.actorIds[objActorNum] + const keyActor = keyActorNum === null ? null : docState.actorIds[keyActorNum] + const ops = { + objActor, objCtr, keyActor, keyCtr, keyStr, idActor: docState.actorIds[idActorNum], idCtr, insert, + objId: objActor === null ? '_root' : `${objCtr}@${objActor}` } - return byteArray -} - -function base64ToBytes (str) { - return base64.toByteArray(base64clean(str)) -} + const {blockIndex, skipCount, visibleCount} = seekToOp(docState, ops) + const block = docState.blocks[blockIndex] + for (let col of block.columns) col.decoder.reset() -function blitBuffer (src, dst, offset, length) { - for (var i = 0; i < length; ++i) { - if ((i + offset >= dst.length) || (i >= src.length)) break - dst[i + offset] = src[i] + const resetFirstVisible = (skipCount === 0) || (block.firstVisibleActor === undefined) || + (!insert && block.firstVisibleActor === keyActorNum && block.firstVisibleCtr === keyCtr) + const newBlock = { + columns: undefined, + bloom: new Uint8Array(block.bloom), + lastKey: copyObject(block.lastKey), + numVisible: copyObject(block.numVisible), + numOps: skipCount, + lastObjectActor: block.lastObjectActor, + lastObjectCtr: block.lastObjectCtr, + firstVisibleActor: resetFirstVisible ? undefined : block.firstVisibleActor, + firstVisibleCtr: resetFirstVisible ? undefined : block.firstVisibleCtr, + lastVisibleActor: undefined, + lastVisibleCtr: undefined } - return i -} - -// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass -// the `instanceof` check but they should be treated as of that type. -// See: https://github.com/feross/buffer/issues/166 -function isInstance (obj, type) { - return obj instanceof type || - (obj != null && obj.constructor != null && obj.constructor.name != null && - obj.constructor.name === type.name) -} -function numberIsNaN (obj) { - // For IE11 support - return obj !== obj // eslint-disable-line no-self-compare -} - -}).call(this)}).call(this,require("buffer").Buffer) -},{"base64-js":88,"buffer":135,"ieee754":242}],136:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -module.exports = Stream; + // Copy the operations up to the insertion position (the first skipCount operations) + const outCols = block.columns.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) + copyColumns(outCols, block.columns, skipCount) -var EE = require('events').EventEmitter; -var inherits = require('inherits'); + // Apply the operations from the change. This may cause blockIndex to move forwards if the + // property being updated straddles a block boundary. + const {blockIndex: lastBlockIndex, docOpsConsumed} = + mergeDocChangeOps(patches, newBlock, outCols, changeState, docState, visibleCount, blockIndex) -inherits(Stream, EE); -Stream.Readable = require('readable-stream/lib/_stream_readable.js'); -Stream.Writable = require('readable-stream/lib/_stream_writable.js'); -Stream.Duplex = require('readable-stream/lib/_stream_duplex.js'); -Stream.Transform = require('readable-stream/lib/_stream_transform.js'); -Stream.PassThrough = require('readable-stream/lib/_stream_passthrough.js'); -Stream.finished = require('readable-stream/lib/internal/streams/end-of-stream.js') -Stream.pipeline = require('readable-stream/lib/internal/streams/pipeline.js') + // Copy the remaining operations after the insertion position + const lastBlock = docState.blocks[lastBlockIndex] + let copyAfterMerge = -skipCount - docOpsConsumed + for (let i = blockIndex; i <= lastBlockIndex; i++) copyAfterMerge += docState.blocks[i].numOps + copyColumns(outCols, lastBlock.columns, copyAfterMerge) + newBlock.numOps += copyAfterMerge -// Backwards-compat with node 0.4.x -Stream.Stream = Stream; + for (let col of lastBlock.columns) { + if (!col.decoder.done) throw new RangeError(`excess ops in column ${col.columnId}`) + } + newBlock.columns = outCols.map(col => { + const decoder = decoderByColumnId(col.columnId, col.encoder.buffer) + return {columnId: col.columnId, decoder} + }) + if (blockIndex === lastBlockIndex && newBlock.numOps <= MAX_BLOCK_SIZE) { + // The result is just one output block + if (copyAfterMerge > 0 && block.lastVisibleActor !== undefined && block.lastVisibleCtr !== undefined) { + // It's possible that none of the ops after the merge point are visible, in which case the + // lastVisible may not be strictly correct, because it may refer to an operation before the + // merge point rather than a list element inserted by the current change. However, this doesn't + // matter, because the only purpose for which we need it is to check whether one block ends with + // the same visible element as the next block starts with (to avoid double-counting its index); + // if the last list element of a block is invisible, the exact value of lastVisible doesn't + // matter since it will be different from the next block's firstVisible in any case. + newBlock.lastVisibleActor = block.lastVisibleActor + newBlock.lastVisibleCtr = block.lastVisibleCtr + } -// old-style streams. Note that the pipe method (the only relevant -// part of this class) is overridden in the Readable class. + docState.blocks[blockIndex] = newBlock -function Stream() { - EE.call(this); + } else { + // Oversized output block must be split into smaller blocks + const newBlocks = splitBlock(newBlock, docState.actorIds) + docState.blocks.splice(blockIndex, lastBlockIndex - blockIndex + 1, ...newBlocks) + } } -Stream.prototype.pipe = function(dest, options) { - var source = this; +/** + * Updates the columns in a document's operation blocks to contain all the columns in a change + * (including any column types we don't recognise, which have been generated by a future version + * of Automerge). + */ +function updateBlockColumns(docState, changeCols) { + // Check that the columns of a change appear at the index at which we expect them to be + if (changeCols[objActorIdx ].columnId !== CHANGE_COLUMNS[objActorIdx ].columnId || CHANGE_COLUMNS[objActorIdx ].columnName !== 'objActor' || + changeCols[objCtrIdx ].columnId !== CHANGE_COLUMNS[objCtrIdx ].columnId || CHANGE_COLUMNS[objCtrIdx ].columnName !== 'objCtr' || + changeCols[keyActorIdx ].columnId !== CHANGE_COLUMNS[keyActorIdx ].columnId || CHANGE_COLUMNS[keyActorIdx ].columnName !== 'keyActor' || + changeCols[keyCtrIdx ].columnId !== CHANGE_COLUMNS[keyCtrIdx ].columnId || CHANGE_COLUMNS[keyCtrIdx ].columnName !== 'keyCtr' || + changeCols[keyStrIdx ].columnId !== CHANGE_COLUMNS[keyStrIdx ].columnId || CHANGE_COLUMNS[keyStrIdx ].columnName !== 'keyStr' || + changeCols[idActorIdx ].columnId !== CHANGE_COLUMNS[idActorIdx ].columnId || CHANGE_COLUMNS[idActorIdx ].columnName !== 'idActor' || + changeCols[idCtrIdx ].columnId !== CHANGE_COLUMNS[idCtrIdx ].columnId || CHANGE_COLUMNS[idCtrIdx ].columnName !== 'idCtr' || + changeCols[insertIdx ].columnId !== CHANGE_COLUMNS[insertIdx ].columnId || CHANGE_COLUMNS[insertIdx ].columnName !== 'insert' || + changeCols[actionIdx ].columnId !== CHANGE_COLUMNS[actionIdx ].columnId || CHANGE_COLUMNS[actionIdx ].columnName !== 'action' || + changeCols[valLenIdx ].columnId !== CHANGE_COLUMNS[valLenIdx ].columnId || CHANGE_COLUMNS[valLenIdx ].columnName !== 'valLen' || + changeCols[valRawIdx ].columnId !== CHANGE_COLUMNS[valRawIdx ].columnId || CHANGE_COLUMNS[valRawIdx ].columnName !== 'valRaw' || + changeCols[predNumIdx ].columnId !== CHANGE_COLUMNS[predNumIdx ].columnId || CHANGE_COLUMNS[predNumIdx ].columnName !== 'predNum' || + changeCols[predActorIdx].columnId !== CHANGE_COLUMNS[predActorIdx].columnId || CHANGE_COLUMNS[predActorIdx].columnName !== 'predActor' || + changeCols[predCtrIdx ].columnId !== CHANGE_COLUMNS[predCtrIdx ].columnId || CHANGE_COLUMNS[predCtrIdx ].columnName !== 'predCtr') { + throw new RangeError('unexpected columnId') + } - function ondata(chunk) { - if (dest.writable) { - if (false === dest.write(chunk) && source.pause) { - source.pause(); + // Check if there any columns in the change that are not in the document, apart from pred* + const docCols = docState.blocks[0].columns + if (!changeCols.every(changeCol => PRED_COLUMN_IDS.includes(changeCol.columnId) || + docCols.find(docCol => docCol.columnId === changeCol.columnId))) { + let allCols = docCols.map(docCol => ({columnId: docCol.columnId})) + for (let changeCol of changeCols) { + const { columnId } = changeCol + if (!PRED_COLUMN_IDS.includes(columnId) && !docCols.find(docCol => docCol.columnId === columnId)) { + allCols.push({columnId}) } } - } - - source.on('data', ondata); + allCols.sort((a, b) => a.columnId - b.columnId) - function ondrain() { - if (source.readable && source.resume) { - source.resume(); + for (let blockIndex = 0; blockIndex < docState.blocks.length; blockIndex++) { + let block = copyObject(docState.blocks[blockIndex]) + block.columns = makeDecoders(block.columns.map(col => ({columnId: col.columnId, buffer: col.decoder.buf})), allCols) + docState.blocks[blockIndex] = block } } +} - dest.on('drain', ondrain); - - // If the 'end' option is not supplied, dest.end() will be called when - // source gets the 'end' or 'close' events. Only dest.end() once. - if (!dest._isStdio && (!options || options.end !== false)) { - source.on('end', onend); - source.on('close', onclose); - } - - var didOnEnd = false; - function onend() { - if (didOnEnd) return; - didOnEnd = true; - - dest.end(); - } - - - function onclose() { - if (didOnEnd) return; - didOnEnd = true; - - if (typeof dest.destroy === 'function') dest.destroy(); +/** + * Takes a decoded change header, including an array of actorIds. Returns an object of the form + * `{actorIds, actorTable}`, where `actorIds` is an updated array of actorIds appearing in the + * document (including the new change's actorId). `actorTable` is an array of integers where + * `actorTable[i]` contains the document's actor index for the actor that has index `i` in the + * change (`i == 0` is the author of the change). + */ +function getActorTable(actorIds, change) { + if (actorIds.indexOf(change.actorIds[0]) < 0) { + if (change.seq !== 1) { + throw new RangeError(`Seq ${change.seq} is the first change for actor ${change.actorIds[0]}`) + } + // Use concat, not push, so that the original array is not mutated + actorIds = actorIds.concat([change.actorIds[0]]) } - - // don't leave dangling pipes when there are errors. - function onerror(er) { - cleanup(); - if (EE.listenerCount(this, 'error') === 0) { - throw er; // Unhandled stream error in pipe. + const actorTable = [] // translate from change's actor index to doc's actor index + for (let actorId of change.actorIds) { + const index = actorIds.indexOf(actorId) + if (index < 0) { + throw new RangeError(`actorId ${actorId} is not known to document`) } + actorTable.push(index) } + return {actorIds, actorTable} +} - source.on('error', onerror); - dest.on('error', onerror); - - // remove all the event listeners that were added. - function cleanup() { - source.removeListener('data', ondata); - dest.removeListener('drain', ondrain); +/** + * Finalises the patch for a change. `patches` is a map from objectIds to patch for that + * particular object, `objectIds` is the array of IDs of objects that are created or updated in the + * change, and `docState` is an object containing various bits of document state, including + * `objectMeta`, a map from objectIds to metadata about that object (such as its parent in the + * document tree). Mutates `patches` such that child objects are linked into their parent object, + * all the way to the root object. + */ +function setupPatches(patches, objectIds, docState) { + for (let objectId of objectIds) { + let meta = docState.objectMeta[objectId], childMeta = null, patchExists = false + while (true) { + const hasChildren = childMeta && Object.keys(meta.children[childMeta.parentKey]).length > 0 + if (!patches[objectId]) patches[objectId] = emptyObjectPatch(objectId, meta.type) - source.removeListener('end', onend); - source.removeListener('close', onclose); + if (childMeta && hasChildren) { + if (meta.type === 'list' || meta.type === 'text') { + // In list/text objects, parentKey is an elemID. First see if it already appears in an edit + for (let edit of patches[objectId].edits) { + if (edit.opId && meta.children[childMeta.parentKey][edit.opId]) { + patchExists = true + } + } - source.removeListener('error', onerror); - dest.removeListener('error', onerror); + // If we need to add an edit, we first have to translate the elemId into an index + if (!patchExists) { + const obj = parseOpId(objectId), elem = parseOpId(childMeta.parentKey) + const seekPos = { + objActor: obj.actorId, objCtr: obj.counter, + keyActor: elem.actorId, keyCtr: elem.counter, + keyStr: null, insert: false, + objId: objectId + } + const { visibleCount } = seekToOp(docState, seekPos) - source.removeListener('end', cleanup); - source.removeListener('close', cleanup); + for (let [opId, value] of Object.entries(meta.children[childMeta.parentKey])) { + let patchValue = value + if (value.objectId) { + if (!patches[value.objectId]) patches[value.objectId] = emptyObjectPatch(value.objectId, value.type) + patchValue = patches[value.objectId] + } + const edit = {action: 'update', index: visibleCount, opId, value: patchValue} + appendEdit(patches[objectId].edits, edit) + } + } - dest.removeListener('close', cleanup); - } + } else { + // Non-list object: parentKey is the name of the property being updated (a string) + if (!patches[objectId].props[childMeta.parentKey]) { + patches[objectId].props[childMeta.parentKey] = {} + } + let values = patches[objectId].props[childMeta.parentKey] - source.on('end', cleanup); - source.on('close', cleanup); + for (let [opId, value] of Object.entries(meta.children[childMeta.parentKey])) { + if (values[opId]) { + patchExists = true + } else if (value.objectId) { + if (!patches[value.objectId]) patches[value.objectId] = emptyObjectPatch(value.objectId, value.type) + values[opId] = patches[value.objectId] + } else { + values[opId] = value + } + } + } + } - dest.on('close', cleanup); + if (patchExists || !meta.parentObj || (childMeta && !hasChildren)) break + childMeta = meta + objectId = meta.parentObj + meta = docState.objectMeta[objectId] + } + } + return patches +} - dest.emit('pipe', source); +/** + * Takes an array of decoded changes and applies them to a document. `docState` contains a bunch of + * fields describing the document state. This function mutates `docState` to contain the updated + * document state, and mutates `patches` to contain a patch to return to the frontend. Only the + * top-level `docState` object is mutated; all nested objects within it are treated as immutable. + * `objectIds` is mutated to contain the IDs of objects that are updated in any of the changes. + * + * Returns a two-element array `[applied, enqueued]`, where `applied` is an array of changes that + * have been applied to the document, and `enqueued` is an array of changes that have not yet been + * applied because they are missing a dependency. + */ +function applyChanges(patches, decodedChanges, docState, objectIds) { + let heads = new Set(docState.heads), changeHashes = new Set() + let clock = copyObject(docState.clock) + let applied = [], enqueued = [] - // Allow for unix-like usage: A.pipe(B).pipe(C) - return dest; -}; + for (let change of decodedChanges) { + // Skip any duplicate changes that we have already seen + if (docState.changeIndexByHash[change.hash] !== undefined || changeHashes.has(change.hash)) continue -},{"events":201,"inherits":243,"readable-stream/lib/_stream_duplex.js":138,"readable-stream/lib/_stream_passthrough.js":139,"readable-stream/lib/_stream_readable.js":140,"readable-stream/lib/_stream_transform.js":141,"readable-stream/lib/_stream_writable.js":142,"readable-stream/lib/internal/streams/end-of-stream.js":146,"readable-stream/lib/internal/streams/pipeline.js":148}],137:[function(require,module,exports){ -arguments[4][119][0].apply(exports,arguments) -},{"dup":119}],138:[function(require,module,exports){ -arguments[4][120][0].apply(exports,arguments) -},{"./_stream_readable":140,"./_stream_writable":142,"_process":377,"dup":120,"inherits":243}],139:[function(require,module,exports){ -arguments[4][121][0].apply(exports,arguments) -},{"./_stream_transform":141,"dup":121,"inherits":243}],140:[function(require,module,exports){ -arguments[4][122][0].apply(exports,arguments) -},{"../errors":137,"./_stream_duplex":138,"./internal/streams/async_iterator":143,"./internal/streams/buffer_list":144,"./internal/streams/destroy":145,"./internal/streams/from":147,"./internal/streams/state":149,"./internal/streams/stream":150,"_process":377,"buffer":135,"dup":122,"events":201,"inherits":243,"string_decoder/":400,"util":91}],141:[function(require,module,exports){ -arguments[4][123][0].apply(exports,arguments) -},{"../errors":137,"./_stream_duplex":138,"dup":123,"inherits":243}],142:[function(require,module,exports){ -arguments[4][124][0].apply(exports,arguments) -},{"../errors":137,"./_stream_duplex":138,"./internal/streams/destroy":145,"./internal/streams/state":149,"./internal/streams/stream":150,"_process":377,"buffer":135,"dup":124,"inherits":243,"util-deprecate":401}],143:[function(require,module,exports){ -arguments[4][125][0].apply(exports,arguments) -},{"./end-of-stream":146,"_process":377,"dup":125}],144:[function(require,module,exports){ -arguments[4][126][0].apply(exports,arguments) -},{"buffer":135,"dup":126,"util":91}],145:[function(require,module,exports){ -arguments[4][127][0].apply(exports,arguments) -},{"_process":377,"dup":127}],146:[function(require,module,exports){ -arguments[4][128][0].apply(exports,arguments) -},{"../../../errors":137,"dup":128}],147:[function(require,module,exports){ -arguments[4][129][0].apply(exports,arguments) -},{"dup":129}],148:[function(require,module,exports){ -arguments[4][130][0].apply(exports,arguments) -},{"../../../errors":137,"./end-of-stream":146,"dup":130}],149:[function(require,module,exports){ -arguments[4][131][0].apply(exports,arguments) -},{"../../../errors":137,"dup":131}],150:[function(require,module,exports){ -arguments[4][132][0].apply(exports,arguments) -},{"dup":132,"events":201}],151:[function(require,module,exports){ -module.exports = function isBuffer(arg) { - return arg && typeof arg === 'object' - && typeof arg.copy === 'function' - && typeof arg.fill === 'function' - && typeof arg.readUInt8 === 'function'; -} -},{}],152:[function(require,module,exports){ -// Currently in sync with Node.js lib/internal/util/types.js -// https://github.com/nodejs/node/commit/112cc7c27551254aa2b17098fb774867f05ed0d9 + let causallyReady = true + for (let dep of change.deps) { + const depIndex = docState.changeIndexByHash[dep] + if ((depIndex === undefined || depIndex === -1) && !changeHashes.has(dep)) { + causallyReady = false + } + } -'use strict'; + if (causallyReady) { + const expectedSeq = (clock[change.actor] || 0) + 1 + if (change.seq !== expectedSeq) { + throw new RangeError(`Expected seq ${expectedSeq}, got seq ${change.seq} from actor ${change.actor}`) + } + clock[change.actor] = change.seq + changeHashes.add(change.hash) + for (let dep of change.deps) heads.delete(dep) + heads.add(change.hash) + applied.push(change) + } else { + enqueued.push(change) + } + } -var isArgumentsObject = require('is-arguments'); -var isGeneratorFunction = require('is-generator-function'); -var whichTypedArray = require('which-typed-array'); -var isTypedArray = require('is-typed-array'); + if (applied.length > 0) { + let changeState = {changes: applied, changeIndex: -1, objectIds} + readNextChangeOp(docState, changeState) + while (!changeState.done) applyOps(patches, changeState, docState) -function uncurryThis(f) { - return f.call.bind(f); + docState.heads = [...heads].sort() + docState.clock = clock + } + return [applied, enqueued] } -var BigIntSupported = typeof BigInt !== 'undefined'; -var SymbolSupported = typeof Symbol !== 'undefined'; - -var ObjectToString = uncurryThis(Object.prototype.toString); +/** + * Scans the operations in a document and generates a patch that can be sent to the frontend to + * instantiate the current state of the document. `objectMeta` is mutated to contain information + * about the parent and children of each object in the document. + */ +function documentPatch(docState) { + for (let col of docState.blocks[0].columns) col.decoder.reset() + let propState = {}, docOp = null, blockIndex = 0 + let patches = {_root: {objectId: '_root', type: 'map', props: {}}} + let lastObjActor = null, lastObjCtr = null, objectId = '_root', elemVisible = false, listIndex = 0 -var numberValue = uncurryThis(Number.prototype.valueOf); -var stringValue = uncurryThis(String.prototype.valueOf); -var booleanValue = uncurryThis(Boolean.prototype.valueOf); + while (true) { + ({ docOp, blockIndex } = readNextDocOp(docState, blockIndex)) + if (docOp === null) break + if (docOp[objActorIdx] !== lastObjActor || docOp[objCtrIdx] !== lastObjCtr) { + objectId = `${docOp[objCtrIdx]}@${docState.actorIds[docOp[objActorIdx]]}` + lastObjActor = docOp[objActorIdx] + lastObjCtr = docOp[objCtrIdx] + propState = {} + listIndex = 0 + elemVisible = false + } -if (BigIntSupported) { - var bigIntValue = uncurryThis(BigInt.prototype.valueOf); -} + if (docOp[insertIdx] && elemVisible) { + elemVisible = false + listIndex++ + } + if (docOp[succNumIdx] === 0) elemVisible = true + if (docOp[idCtrIdx] > docState.maxOp) docState.maxOp = docOp[idCtrIdx] + for (let i = 0; i < docOp[succNumIdx]; i++) { + if (docOp[succCtrIdx][i] > docState.maxOp) docState.maxOp = docOp[succCtrIdx][i] + } -if (SymbolSupported) { - var symbolValue = uncurryThis(Symbol.prototype.valueOf); + updatePatchProperty(patches, null, objectId, docOp, docState, propState, listIndex, docOp[succNumIdx]) + } + return patches._root } -function checkBoxedPrimitive(value, prototypeValueOf) { - if (typeof value !== 'object') { - return false; +/** + * Takes an encoded document whose headers have been parsed using `decodeDocumentHeader()` and reads + * from it the list of changes. Returns the document's current vector clock, i.e. an object mapping + * each actor ID (as a hex string) to the number of changes seen from that actor. Also returns an + * array of the actorIds whose most recent change has no dependents (i.e. the actors that + * contributed the current heads of the document), and an array of encoders that has been + * initialised to contain the columns of the changes list. + */ +function readDocumentChanges(doc) { + const columns = makeDecoders(doc.changesColumns, DOCUMENT_COLUMNS) + const actorD = columns[0].decoder, seqD = columns[1].decoder + const depsNumD = columns[5].decoder, depsIndexD = columns[6].decoder + if (columns[0].columnId !== DOCUMENT_COLUMNS[0].columnId || DOCUMENT_COLUMNS[0].columnName !== 'actor' || + columns[1].columnId !== DOCUMENT_COLUMNS[1].columnId || DOCUMENT_COLUMNS[1].columnName !== 'seq' || + columns[5].columnId !== DOCUMENT_COLUMNS[5].columnId || DOCUMENT_COLUMNS[5].columnName !== 'depsNum' || + columns[6].columnId !== DOCUMENT_COLUMNS[6].columnId || DOCUMENT_COLUMNS[6].columnName !== 'depsIndex') { + throw new RangeError('unexpected columnId') } - try { - prototypeValueOf(value); - return true; - } catch(e) { - return false; + + let numChanges = 0, clock = {}, actorNums = [], headIndexes = new Set() + while (!actorD.done) { + const actorNum = actorD.readValue(), seq = seqD.readValue(), depsNum = depsNumD.readValue() + const actorId = doc.actorIds[actorNum] + if (seq !== 1 && seq !== clock[actorId] + 1) { + throw new RangeError(`Expected seq ${clock[actorId] + 1}, got ${seq} for actor ${actorId}`) + } + actorNums.push(actorNum) + clock[actorId] = seq + headIndexes.add(numChanges) + for (let j = 0; j < depsNum; j++) headIndexes.delete(depsIndexD.readValue()) + numChanges++ } -} + const headActors = [...headIndexes].map(index => doc.actorIds[actorNums[index]]).sort() -exports.isArgumentsObject = isArgumentsObject; -exports.isGeneratorFunction = isGeneratorFunction; -exports.isTypedArray = isTypedArray; + for (let col of columns) col.decoder.reset() + const encoders = columns.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) + copyColumns(encoders, columns, numChanges) + return {clock, headActors, encoders, numChanges} +} -// Taken from here and modified for better browser support -// https://github.com/sindresorhus/p-is-promise/blob/cda35a513bda03f977ad5cde3a079d237e82d7ef/index.js -function isPromise(input) { - return ( - ( - typeof Promise !== 'undefined' && - input instanceof Promise - ) || - ( - input !== null && - typeof input === 'object' && - typeof input.then === 'function' && - typeof input.catch === 'function' - ) - ); +/** + * Records the metadata about a change in the appropriate columns. + */ +function appendChange(columns, change, actorIds, changeIndexByHash) { + appendOperation(columns, DOCUMENT_COLUMNS, [ + actorIds.indexOf(change.actor), // actor + change.seq, // seq + change.maxOp, // maxOp + change.time, // time + change.message, // message + change.deps.length, // depsNum + change.deps.map(dep => changeIndexByHash[dep]), // depsIndex + change.extraBytes ? (change.extraBytes.byteLength << 4 | VALUE_TYPE.BYTES) : VALUE_TYPE.BYTES, // extraLen + change.extraBytes // extraRaw + ]) } -exports.isPromise = isPromise; -function isArrayBufferView(value) { - if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView) { - return ArrayBuffer.isView(value); - } +class BackendDoc { + constructor(buffer) { + this.maxOp = 0 + this.haveHashGraph = false + this.changes = [] + this.changeIndexByHash = {} + this.dependenciesByHash = {} + this.dependentsByHash = {} + this.hashesByActor = {} + this.actorIds = [] + this.heads = [] + this.clock = {} + this.queue = [] + this.objectMeta = {_root: {parentObj: null, parentKey: null, opId: null, type: 'map', children: {}}} - return ( - isTypedArray(value) || - isDataView(value) - ); -} -exports.isArrayBufferView = isArrayBufferView; + if (buffer) { + const doc = decodeDocumentHeader(buffer) + const {clock, headActors, encoders, numChanges} = readDocumentChanges(doc) + this.binaryDoc = buffer + this.changes = new Array(numChanges) + this.actorIds = doc.actorIds + this.heads = doc.heads + this.clock = clock + this.changesEncoders = encoders + this.extraBytes = doc.extraBytes + // If there is a single head, we can unambiguously point at the actorId and sequence number of + // the head hash without having to reconstruct the hash graph + if (doc.heads.length === 1 && headActors.length === 1) { + this.hashesByActor[headActors[0]] = [] + this.hashesByActor[headActors[0]][clock[headActors[0]] - 1] = doc.heads[0] + } -function isUint8Array(value) { - return whichTypedArray(value) === 'Uint8Array'; -} -exports.isUint8Array = isUint8Array; + // The encoded document gives each change an index, and expresses dependencies in terms of + // those indexes. Initialise the translation table from hash to index. + if (doc.heads.length === doc.headsIndexes.length) { + for (let i = 0; i < doc.heads.length; i++) { + this.changeIndexByHash[doc.heads[i]] = doc.headsIndexes[i] + } + } else if (doc.heads.length === 1) { + // If there is only one head, it must be the last change + this.changeIndexByHash[doc.heads[0]] = numChanges - 1 + } else { + // We know the heads hashes, but not their indexes + for (let head of doc.heads) this.changeIndexByHash[head] = -1 + } -function isUint8ClampedArray(value) { - return whichTypedArray(value) === 'Uint8ClampedArray'; -} -exports.isUint8ClampedArray = isUint8ClampedArray; + this.blocks = [{columns: makeDecoders(doc.opsColumns, DOC_OPS_COLUMNS)}] + updateBlockMetadata(this.blocks[0], this.actorIds) + if (this.blocks[0].numOps > MAX_BLOCK_SIZE) { + this.blocks = splitBlock(this.blocks[0], this.actorIds) + } -function isUint16Array(value) { - return whichTypedArray(value) === 'Uint16Array'; -} -exports.isUint16Array = isUint16Array; + let docState = {blocks: this.blocks, actorIds: this.actorIds, objectMeta: this.objectMeta, maxOp: 0} + this.initPatch = documentPatch(docState) + this.maxOp = docState.maxOp -function isUint32Array(value) { - return whichTypedArray(value) === 'Uint32Array'; -} -exports.isUint32Array = isUint32Array; + } else { + this.haveHashGraph = true + this.changesEncoders = DOCUMENT_COLUMNS.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) + this.blocks = [{ + columns: makeDecoders([], DOC_OPS_COLUMNS), + bloom: new Uint8Array(BLOOM_FILTER_SIZE), + lastKey: {}, + numVisible: {}, + numOps: 0, + lastObjectActor: undefined, + lastObjectCtr: undefined, + firstVisibleActor: undefined, + firstVisibleCtr: undefined, + lastVisibleActor: undefined, + lastVisibleCtr: undefined + }] + } + } -function isInt8Array(value) { - return whichTypedArray(value) === 'Int8Array'; -} -exports.isInt8Array = isInt8Array; + /** + * Makes a copy of this BackendDoc that can be independently modified. + */ + clone() { + let copy = new BackendDoc() + copy.maxOp = this.maxOp + copy.haveHashGraph = this.haveHashGraph + copy.changes = this.changes.slice() + copy.changeIndexByHash = copyObject(this.changeIndexByHash) + copy.dependenciesByHash = copyObject(this.dependenciesByHash) + copy.dependentsByHash = Object.entries(this.dependentsByHash).reduce((acc, [k, v]) => { acc[k] = v.slice(); return acc }, {}) + copy.hashesByActor = Object.entries(this.hashesByActor).reduce((acc, [k, v]) => { acc[k] = v.slice(); return acc }, {}) + copy.actorIds = this.actorIds // immutable, no copying needed + copy.heads = this.heads // immutable, no copying needed + copy.clock = this.clock // immutable, no copying needed + copy.blocks = this.blocks // immutable, no copying needed + copy.objectMeta = this.objectMeta // immutable, no copying needed + copy.queue = this.queue // immutable, no copying needed + return copy + } -function isInt16Array(value) { - return whichTypedArray(value) === 'Int16Array'; -} -exports.isInt16Array = isInt16Array; + /** + * Parses the changes given as Uint8Arrays in `changeBuffers`, and applies them to the current + * document. Returns a patch to apply to the frontend. If an exception is thrown, the document + * object is not modified. + */ + applyChanges(changeBuffers, isLocal = false) { + // decoded change has the form { actor, seq, startOp, time, message, deps, actorIds, hash, columns, buffer } + let decodedChanges = changeBuffers.map(buffer => { + const decoded = decodeChangeColumns(buffer) + decoded.buffer = buffer + return decoded + }) -function isInt32Array(value) { - return whichTypedArray(value) === 'Int32Array'; -} -exports.isInt32Array = isInt32Array; + let patches = {_root: {objectId: '_root', type: 'map', props: {}}} + let docState = { + maxOp: this.maxOp, + changeIndexByHash: this.changeIndexByHash, + actorIds: this.actorIds, + heads: this.heads, + clock: this.clock, + blocks: this.blocks.slice(), + objectMeta: Object.assign({}, this.objectMeta) + } + let queue = (this.queue.length === 0) ? decodedChanges : decodedChanges.concat(this.queue) + let allApplied = [], objectIds = new Set() -function isFloat32Array(value) { - return whichTypedArray(value) === 'Float32Array'; -} -exports.isFloat32Array = isFloat32Array; + while (true) { + const [applied, enqueued] = applyChanges(patches, queue, docState, objectIds) + queue = enqueued + if (applied.length > 0) allApplied = allApplied.concat(applied) + if (queue.length === 0) break -function isFloat64Array(value) { - return whichTypedArray(value) === 'Float64Array'; -} -exports.isFloat64Array = isFloat64Array; + // If we are missing a dependency, and we haven't computed the hash graph yet, first compute + // the hashes to see if we actually have it already + if (applied.length === 0) { + if (!this.haveHashGraph) this.computeHashGraph(); else break + } + } -function isBigInt64Array(value) { - return whichTypedArray(value) === 'BigInt64Array'; -} -exports.isBigInt64Array = isBigInt64Array; + setupPatches(patches, objectIds, docState) -function isBigUint64Array(value) { - return whichTypedArray(value) === 'BigUint64Array'; -} -exports.isBigUint64Array = isBigUint64Array; + // Update the document state only if `applyChanges` does not throw an exception + for (let change of allApplied) { + this.changes.push(change.buffer) + if (!this.hashesByActor[change.actor]) this.hashesByActor[change.actor] = [] + this.hashesByActor[change.actor][change.seq - 1] = change.hash + this.changeIndexByHash[change.hash] = this.changes.length - 1 + this.dependenciesByHash[change.hash] = change.deps + this.dependentsByHash[change.hash] = [] + for (let dep of change.deps) { + if (!this.dependentsByHash[dep]) this.dependentsByHash[dep] = [] + this.dependentsByHash[dep].push(change.hash) + } + appendChange(this.changesEncoders, change, docState.actorIds, this.changeIndexByHash) + } -function isMapToString(value) { - return ObjectToString(value) === '[object Map]'; -} -isMapToString.working = ( - typeof Map !== 'undefined' && - isMapToString(new Map()) -); + this.maxOp = docState.maxOp + this.actorIds = docState.actorIds + this.heads = docState.heads + this.clock = docState.clock + this.blocks = docState.blocks + this.objectMeta = docState.objectMeta + this.queue = queue + this.binaryDoc = null + this.initPatch = null -function isMap(value) { - if (typeof Map === 'undefined') { - return false; + let patch = { + maxOp: this.maxOp, clock: this.clock, deps: this.heads, + pendingChanges: this.queue.length, diffs: patches._root + } + if (isLocal && decodedChanges.length === 1) { + patch.actor = decodedChanges[0].actor + patch.seq = decodedChanges[0].seq + } + return patch } - return isMapToString.working - ? isMapToString(value) - : value instanceof Map; -} -exports.isMap = isMap; + /** + * Reconstructs the full change history of a document, and initialises the variables that allow us + * to traverse the hash graph of changes and their dependencies. When a compressed document is + * loaded we defer the computation of this hash graph to make loading faster, but if the hash + * graph is later needed (e.g. for the sync protocol), this function fills it in. + */ + computeHashGraph() { + const binaryDoc = this.save() + this.haveHashGraph = true + this.changes = [] + this.changeIndexByHash = {} + this.dependenciesByHash = {} + this.dependentsByHash = {} + this.hashesByActor = {} + this.clock = {} -function isSetToString(value) { - return ObjectToString(value) === '[object Set]'; -} -isSetToString.working = ( - typeof Set !== 'undefined' && - isSetToString(new Set()) -); -function isSet(value) { - if (typeof Set === 'undefined') { - return false; + for (let change of decodeChanges([binaryDoc])) { + const binaryChange = encodeChange(change) // TODO: avoid decoding and re-encoding again + this.changes.push(binaryChange) + this.changeIndexByHash[change.hash] = this.changes.length - 1 + this.dependenciesByHash[change.hash] = change.deps + this.dependentsByHash[change.hash] = [] + for (let dep of change.deps) this.dependentsByHash[dep].push(change.hash) + if (change.seq === 1) this.hashesByActor[change.actor] = [] + this.hashesByActor[change.actor].push(change.hash) + const expectedSeq = (this.clock[change.actor] || 0) + 1 + if (change.seq !== expectedSeq) { + throw new RangeError(`Expected seq ${expectedSeq}, got seq ${change.seq} from actor ${change.actor}`) + } + this.clock[change.actor] = change.seq + } } - return isSetToString.working - ? isSetToString(value) - : value instanceof Set; -} -exports.isSet = isSet; + /** + * Returns all the changes that need to be sent to another replica. `haveDeps` is a list of change + * hashes (as hex strings) of the heads that the other replica has. The changes in `haveDeps` and + * any of their transitive dependencies will not be returned; any changes later than or concurrent + * to the hashes in `haveDeps` will be returned. If `haveDeps` is an empty array, all changes are + * returned. Throws an exception if any of the given hashes are not known to this replica. + */ + getChanges(haveDeps) { + if (!this.haveHashGraph) this.computeHashGraph() -function isWeakMapToString(value) { - return ObjectToString(value) === '[object WeakMap]'; -} -isWeakMapToString.working = ( - typeof WeakMap !== 'undefined' && - isWeakMapToString(new WeakMap()) -); -function isWeakMap(value) { - if (typeof WeakMap === 'undefined') { - return false; - } + // If the other replica has nothing, return all changes in history order + if (haveDeps.length === 0) { + return this.changes.slice() + } - return isWeakMapToString.working - ? isWeakMapToString(value) - : value instanceof WeakMap; -} -exports.isWeakMap = isWeakMap; + // Fast path for the common case where all new changes depend only on haveDeps + let stack = [], seenHashes = {}, toReturn = [] + for (let hash of haveDeps) { + seenHashes[hash] = true + const successors = this.dependentsByHash[hash] + if (!successors) throw new RangeError(`hash not found: ${hash}`) + stack.push(...successors) + } -function isWeakSetToString(value) { - return ObjectToString(value) === '[object WeakSet]'; -} -isWeakSetToString.working = ( - typeof WeakSet !== 'undefined' && - isWeakSetToString(new WeakSet()) -); -function isWeakSet(value) { - return isWeakSetToString(value); -} -exports.isWeakSet = isWeakSet; + // Depth-first traversal of the hash graph to find all changes that depend on `haveDeps` + while (stack.length > 0) { + const hash = stack.pop() + seenHashes[hash] = true + toReturn.push(hash) + if (!this.dependenciesByHash[hash].every(dep => seenHashes[dep])) { + // If a change depends on a hash we have not seen, abort the traversal and fall back to the + // slower algorithm. This will sometimes abort even if all new changes depend on `haveDeps`, + // because our depth-first traversal is not necessarily a topological sort of the graph. + break + } + stack.push(...this.dependentsByHash[hash]) + } -function isArrayBufferToString(value) { - return ObjectToString(value) === '[object ArrayBuffer]'; -} -isArrayBufferToString.working = ( - typeof ArrayBuffer !== 'undefined' && - isArrayBufferToString(new ArrayBuffer()) -); -function isArrayBuffer(value) { - if (typeof ArrayBuffer === 'undefined') { - return false; - } + // If the traversal above has encountered all the heads, and was not aborted early due to + // a missing dependency, then the set of changes it has found is complete, so we can return it + if (stack.length === 0 && this.heads.every(head => seenHashes[head])) { + return toReturn.map(hash => this.changes[this.changeIndexByHash[hash]]) + } - return isArrayBufferToString.working - ? isArrayBufferToString(value) - : value instanceof ArrayBuffer; -} -exports.isArrayBuffer = isArrayBuffer; + // If we haven't encountered all of the heads, we have to search harder. This will happen if + // changes were added that are concurrent to `haveDeps` + stack = haveDeps.slice() + seenHashes = {} + while (stack.length > 0) { + const hash = stack.pop() + if (!seenHashes[hash]) { + const deps = this.dependenciesByHash[hash] + if (!deps) throw new RangeError(`hash not found: ${hash}`) + stack.push(...deps) + seenHashes[hash] = true + } + } -function isDataViewToString(value) { - return ObjectToString(value) === '[object DataView]'; -} -isDataViewToString.working = ( - typeof ArrayBuffer !== 'undefined' && - typeof DataView !== 'undefined' && - isDataViewToString(new DataView(new ArrayBuffer(1), 0, 1)) -); -function isDataView(value) { - if (typeof DataView === 'undefined') { - return false; + return this.changes.filter(change => !seenHashes[decodeChangeMeta(change, true).hash]) } - return isDataViewToString.working - ? isDataViewToString(value) - : value instanceof DataView; -} -exports.isDataView = isDataView; + /** + * Returns all changes that are present in this BackendDoc, but not present in the `other` + * BackendDoc. + */ + getChangesAdded(other) { + if (!this.haveHashGraph) this.computeHashGraph() -// Store a copy of SharedArrayBuffer in case it's deleted elsewhere -var SharedArrayBufferCopy = typeof SharedArrayBuffer !== 'undefined' ? SharedArrayBuffer : undefined; -function isSharedArrayBufferToString(value) { - return ObjectToString(value) === '[object SharedArrayBuffer]'; -} -function isSharedArrayBuffer(value) { - if (typeof SharedArrayBufferCopy === 'undefined') { - return false; - } + // Depth-first traversal from the heads through the dependency graph, + // until we reach a change that is already present in opSet1 + let stack = this.heads.slice(), seenHashes = {}, toReturn = [] + while (stack.length > 0) { + const hash = stack.pop() + if (!seenHashes[hash] && other.changeIndexByHash[hash] === undefined) { + seenHashes[hash] = true + toReturn.push(hash) + stack.push(...this.dependenciesByHash[hash]) + } + } - if (typeof isSharedArrayBufferToString.working === 'undefined') { - isSharedArrayBufferToString.working = isSharedArrayBufferToString(new SharedArrayBufferCopy()); + // Return those changes in the reverse of the order in which the depth-first search + // found them. This is not necessarily a topological sort, but should usually be close. + return toReturn.reverse().map(hash => this.changes[this.changeIndexByHash[hash]]) } - return isSharedArrayBufferToString.working - ? isSharedArrayBufferToString(value) - : value instanceof SharedArrayBufferCopy; -} -exports.isSharedArrayBuffer = isSharedArrayBuffer; + getChangeByHash(hash) { + if (!this.haveHashGraph) this.computeHashGraph() + return this.changes[this.changeIndexByHash[hash]] + } -function isAsyncFunction(value) { - return ObjectToString(value) === '[object AsyncFunction]'; -} -exports.isAsyncFunction = isAsyncFunction; + /** + * Returns the hashes of any missing dependencies, i.e. where we have tried to apply a change that + * has a dependency on a change we have not seen. + * + * If the argument `heads` is given (an array of hexadecimal strings representing hashes as + * returned by `getHeads()`), this function also ensures that all of those hashes resolve to + * either a change that has been applied to the document, or that has been enqueued for later + * application once missing dependencies have arrived. Any missing heads hashes are included in + * the returned array. + */ + getMissingDeps(heads = []) { + if (!this.haveHashGraph) this.computeHashGraph() -function isMapIterator(value) { - return ObjectToString(value) === '[object Map Iterator]'; -} -exports.isMapIterator = isMapIterator; + let allDeps = new Set(heads), inQueue = new Set() + for (let change of this.queue) { + inQueue.add(change.hash) + for (let dep of change.deps) allDeps.add(dep) + } -function isSetIterator(value) { - return ObjectToString(value) === '[object Set Iterator]'; -} -exports.isSetIterator = isSetIterator; + let missing = [] + for (let hash of allDeps) { + if (this.changeIndexByHash[hash] === undefined && !inQueue.has(hash)) missing.push(hash) + } + return missing.sort() + } -function isGeneratorObject(value) { - return ObjectToString(value) === '[object Generator]'; -} -exports.isGeneratorObject = isGeneratorObject; + /** + * Serialises the current document state into a single byte array. + */ + save() { + if (this.binaryDoc) return this.binaryDoc -function isWebAssemblyCompiledModule(value) { - return ObjectToString(value) === '[object WebAssembly.Module]'; -} -exports.isWebAssemblyCompiledModule = isWebAssemblyCompiledModule; + // Getting the byte array for the changes columns finalises their encoders, after which we can + // no longer append values to them. We therefore copy their data over to fresh encoders. + const newEncoders = this.changesEncoders.map(col => ({columnId: col.columnId, encoder: encoderByColumnId(col.columnId)})) + const decoders = this.changesEncoders.map(col => { + const decoder = decoderByColumnId(col.columnId, col.encoder.buffer) + return {columnId: col.columnId, decoder} + }) + copyColumns(newEncoders, decoders, this.changes.length) -function isNumberObject(value) { - return checkBoxedPrimitive(value, numberValue); -} -exports.isNumberObject = isNumberObject; + this.binaryDoc = encodeDocumentHeader({ + changesColumns: this.changesEncoders, + opsColumns: concatBlocks(this.blocks), + actorIds: this.actorIds, // TODO: sort actorIds (requires transforming all actorId columns in opsColumns) + heads: this.heads, + headsIndexes: this.heads.map(hash => this.changeIndexByHash[hash]), + extraBytes: this.extraBytes + }) + this.changesEncoders = newEncoders + return this.binaryDoc + } -function isStringObject(value) { - return checkBoxedPrimitive(value, stringValue); + /** + * Returns a patch from which we can initialise the current state of the backend. + */ + getPatch() { + const objectMeta = {_root: {parentObj: null, parentKey: null, opId: null, type: 'map', children: {}}} + const docState = {blocks: this.blocks, actorIds: this.actorIds, objectMeta, maxOp: 0} + const diffs = this.initPatch ? this.initPatch : documentPatch(docState) + return { + maxOp: this.maxOp, clock: this.clock, deps: this.heads, + pendingChanges: this.queue.length, diffs + } + } } -exports.isStringObject = isStringObject; -function isBooleanObject(value) { - return checkBoxedPrimitive(value, booleanValue); -} -exports.isBooleanObject = isBooleanObject; +module.exports = { MAX_BLOCK_SIZE, BackendDoc, bloomFilterContains } -function isBigIntObject(value) { - return BigIntSupported && checkBoxedPrimitive(value, bigIntValue); -} -exports.isBigIntObject = isBigIntObject; +},{"../src/common":101,"./columnar":84}],88:[function(require,module,exports){ +/** + * Implementation of the data synchronisation protocol that brings a local and a remote document + * into the same state. This is typically used when two nodes have been disconnected for some time, + * and need to exchange any changes that happened while they were disconnected. The two nodes that + * are syncing could be client and server, or server and client, or two peers with symmetric roles. + * + * The protocol is based on this paper: Martin Kleppmann and Heidi Howard. Byzantine Eventual + * Consistency and the Fundamental Limits of Peer-to-Peer Databases. https://arxiv.org/abs/2012.00472 + * + * The protocol assumes that every time a node successfully syncs with another node, it remembers + * the current heads (as returned by `Backend.getHeads()`) after the last sync with that node. The + * next time we try to sync with the same node, we start from the assumption that the other node's + * document version is no older than the outcome of the last sync, so we only need to exchange any + * changes that are more recent than the last sync. This assumption may not be true if the other + * node did not correctly persist its state (perhaps it crashed before writing the result of the + * last sync to disk), and we fall back to sending the entire document in this case. + */ -function isSymbolObject(value) { - return SymbolSupported && checkBoxedPrimitive(value, symbolValue); -} -exports.isSymbolObject = isSymbolObject; +const Backend = require('./backend') +const { hexStringToBytes, bytesToHexString, Encoder, Decoder } = require('./encoding') +const { decodeChangeMeta } = require('./columnar') +const { copyObject } = require('../src/common') -function isBoxedPrimitive(value) { - return ( - isNumberObject(value) || - isStringObject(value) || - isBooleanObject(value) || - isBigIntObject(value) || - isSymbolObject(value) - ); -} -exports.isBoxedPrimitive = isBoxedPrimitive; +const HASH_SIZE = 32 // 256 bits = 32 bytes +const MESSAGE_TYPE_SYNC = 0x42 // first byte of a sync message, for identification +const PEER_STATE_TYPE = 0x43 // first byte of an encoded peer state, for identification -function isAnyArrayBuffer(value) { - return typeof Uint8Array !== 'undefined' && ( - isArrayBuffer(value) || - isSharedArrayBuffer(value) - ); -} -exports.isAnyArrayBuffer = isAnyArrayBuffer; +// These constants correspond to a 1% false positive rate. The values can be changed without +// breaking compatibility of the network protocol, since the parameters used for a particular +// Bloom filter are encoded in the wire format. +const BITS_PER_ENTRY = 10, NUM_PROBES = 7 -['isProxy', 'isExternal', 'isModuleNamespaceObject'].forEach(function(method) { - Object.defineProperty(exports, method, { - enumerable: false, - value: function() { - throw new Error(method + ' is not supported in userland'); +/** + * A Bloom filter implementation that can be serialised to a byte array for transmission + * over a network. The entries that are added are assumed to already be SHA-256 hashes, + * so this implementation does not perform its own hashing. + */ +class BloomFilter { + constructor (arg) { + if (Array.isArray(arg)) { + // arg is an array of SHA256 hashes in hexadecimal encoding + this.numEntries = arg.length + this.numBitsPerEntry = BITS_PER_ENTRY + this.numProbes = NUM_PROBES + this.bits = new Uint8Array(Math.ceil(this.numEntries * this.numBitsPerEntry / 8)) + for (let hash of arg) this.addHash(hash) + } else if (arg instanceof Uint8Array) { + if (arg.byteLength === 0) { + this.numEntries = 0 + this.numBitsPerEntry = 0 + this.numProbes = 0 + this.bits = arg + } else { + const decoder = new Decoder(arg) + this.numEntries = decoder.readUint32() + this.numBitsPerEntry = decoder.readUint32() + this.numProbes = decoder.readUint32() + this.bits = decoder.readRawBytes(Math.ceil(this.numEntries * this.numBitsPerEntry / 8)) + } + } else { + throw new TypeError('invalid argument') } - }); -}); + } -},{"is-arguments":245,"is-generator-function":248,"is-typed-array":249,"which-typed-array":402}],153:[function(require,module,exports){ -(function (process){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. + /** + * Returns the Bloom filter state, encoded as a byte array. + */ + get bytes() { + if (this.numEntries === 0) return new Uint8Array(0) + const encoder = new Encoder() + encoder.appendUint32(this.numEntries) + encoder.appendUint32(this.numBitsPerEntry) + encoder.appendUint32(this.numProbes) + encoder.appendRawBytes(this.bits) + return encoder.buffer + } -var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || - function getOwnPropertyDescriptors(obj) { - var keys = Object.keys(obj); - var descriptors = {}; - for (var i = 0; i < keys.length; i++) { - descriptors[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i]); + /** + * Given a SHA-256 hash (as hex string), returns an array of probe indexes indicating which bits + * in the Bloom filter need to be tested or set for this particular entry. We do this by + * interpreting the first 12 bytes of the hash as three little-endian 32-bit unsigned integers, + * and then using triple hashing to compute the probe indexes. The algorithm comes from: + * + * Peter C. Dillinger and Panagiotis Manolios. Bloom Filters in Probabilistic Verification. + * 5th International Conference on Formal Methods in Computer-Aided Design (FMCAD), November 2004. + * http://www.ccis.northeastern.edu/home/pete/pub/bloom-filters-verification.pdf + */ + getProbes(hash) { + const hashBytes = hexStringToBytes(hash), modulo = 8 * this.bits.byteLength + if (hashBytes.byteLength !== 32) throw new RangeError(`Not a 256-bit hash: ${hash}`) + // on the next three lines, the right shift means interpret value as unsigned + let x = ((hashBytes[0] | hashBytes[1] << 8 | hashBytes[2] << 16 | hashBytes[3] << 24) >>> 0) % modulo + let y = ((hashBytes[4] | hashBytes[5] << 8 | hashBytes[6] << 16 | hashBytes[7] << 24) >>> 0) % modulo + let z = ((hashBytes[8] | hashBytes[9] << 8 | hashBytes[10] << 16 | hashBytes[11] << 24) >>> 0) % modulo + const probes = [x] + for (let i = 1; i < this.numProbes; i++) { + x = (x + y) % modulo + y = (y + z) % modulo + probes.push(x) } - return descriptors; - }; + return probes + } -var formatRegExp = /%[sdj%]/g; -exports.format = function(f) { - if (!isString(f)) { - var objects = []; - for (var i = 0; i < arguments.length; i++) { - objects.push(inspect(arguments[i])); + /** + * Sets the Bloom filter bits corresponding to a given SHA-256 hash (given as hex string). + */ + addHash(hash) { + for (let probe of this.getProbes(hash)) { + this.bits[probe >>> 3] |= 1 << (probe & 7) } - return objects.join(' '); } - var i = 1; - var args = arguments; - var len = args.length; - var str = String(f).replace(formatRegExp, function(x) { - if (x === '%%') return '%'; - if (i >= len) return x; - switch (x) { - case '%s': return String(args[i++]); - case '%d': return Number(args[i++]); - case '%j': - try { - return JSON.stringify(args[i++]); - } catch (_) { - return '[Circular]'; - } - default: - return x; - } - }); - for (var x = args[i]; i < len; x = args[++i]) { - if (isNull(x) || !isObject(x)) { - str += ' ' + x; - } else { - str += ' ' + inspect(x); + /** + * Tests whether a given SHA-256 hash (given as hex string) is contained in the Bloom filter. + */ + containsHash(hash) { + if (this.numEntries === 0) return false + for (let probe of this.getProbes(hash)) { + if ((this.bits[probe >>> 3] & (1 << (probe & 7))) === 0) { + return false + } } + return true } - return str; -}; - +} -// Mark that a method should not be used. -// Returns a modified function which warns once by default. -// If --no-deprecation is set, then it is a no-op. -exports.deprecate = function(fn, msg) { - if (typeof process !== 'undefined' && process.noDeprecation === true) { - return fn; +/** + * Encodes a sorted array of SHA-256 hashes (as hexadecimal strings) into a byte array. + */ +function encodeHashes(encoder, hashes) { + if (!Array.isArray(hashes)) throw new TypeError('hashes must be an array') + encoder.appendUint32(hashes.length) + for (let i = 0; i < hashes.length; i++) { + if (i > 0 && hashes[i - 1] >= hashes[i]) throw new RangeError('hashes must be sorted') + const bytes = hexStringToBytes(hashes[i]) + if (bytes.byteLength !== HASH_SIZE) throw new TypeError('heads hashes must be 256 bits') + encoder.appendRawBytes(bytes) } +} - // Allow for deprecating things in the process of starting up. - if (typeof process === 'undefined') { - return function() { - return exports.deprecate(fn, msg).apply(this, arguments); - }; +/** + * Decodes a byte array in the format returned by encodeHashes(), and returns its content as an + * array of hex strings. + */ +function decodeHashes(decoder) { + let length = decoder.readUint32(), hashes = [] + for (let i = 0; i < length; i++) { + hashes.push(bytesToHexString(decoder.readRawBytes(HASH_SIZE))) } + return hashes +} - var warned = false; - function deprecated() { - if (!warned) { - if (process.throwDeprecation) { - throw new Error(msg); - } else if (process.traceDeprecation) { - console.trace(msg); - } else { - console.error(msg); - } - warned = true; - } - return fn.apply(this, arguments); +/** + * Takes a sync message of the form `{heads, need, have, changes}` and encodes it as a byte array for + * transmission. + */ +function encodeSyncMessage(message) { + const encoder = new Encoder() + encoder.appendByte(MESSAGE_TYPE_SYNC) + encodeHashes(encoder, message.heads) + encodeHashes(encoder, message.need) + encoder.appendUint32(message.have.length) + for (let have of message.have) { + encodeHashes(encoder, have.lastSync) + encoder.appendPrefixedBytes(have.bloom) } - - return deprecated; -}; - - -var debugs = {}; -var debugEnvRegex = /^$/; - -if (process.env.NODE_DEBUG) { - var debugEnv = process.env.NODE_DEBUG; - debugEnv = debugEnv.replace(/[|\\{}()[\]^$+?.]/g, '\\$&') - .replace(/\*/g, '.*') - .replace(/,/g, '$|^') - .toUpperCase(); - debugEnvRegex = new RegExp('^' + debugEnv + '$', 'i'); -} -exports.debuglog = function(set) { - set = set.toUpperCase(); - if (!debugs[set]) { - if (debugEnvRegex.test(set)) { - var pid = process.pid; - debugs[set] = function() { - var msg = exports.format.apply(exports, arguments); - console.error('%s %d: %s', set, pid, msg); - }; - } else { - debugs[set] = function() {}; - } + encoder.appendUint32(message.changes.length) + for (let change of message.changes) { + encoder.appendPrefixedBytes(change) } - return debugs[set]; -}; - + return encoder.buffer +} /** - * Echos the value of a value. Trys to print the value out - * in the best way possible given the different types. - * - * @param {Object} obj The object to print out. - * @param {Object} opts Optional options object that alters the output. + * Takes a binary-encoded sync message and decodes it into the form `{heads, need, have, changes}`. */ -/* legacy: obj, showHidden, depth, colors*/ -function inspect(obj, opts) { - // default options - var ctx = { - seen: [], - stylize: stylizeNoColor - }; - // legacy... - if (arguments.length >= 3) ctx.depth = arguments[2]; - if (arguments.length >= 4) ctx.colors = arguments[3]; - if (isBoolean(opts)) { - // legacy... - ctx.showHidden = opts; - } else if (opts) { - // got an "options" object - exports._extend(ctx, opts); +function decodeSyncMessage(bytes) { + const decoder = new Decoder(bytes) + const messageType = decoder.readByte() + if (messageType !== MESSAGE_TYPE_SYNC) { + throw new RangeError(`Unexpected message type: ${messageType}`) } - // set default options - if (isUndefined(ctx.showHidden)) ctx.showHidden = false; - if (isUndefined(ctx.depth)) ctx.depth = 2; - if (isUndefined(ctx.colors)) ctx.colors = false; - if (isUndefined(ctx.customInspect)) ctx.customInspect = true; - if (ctx.colors) ctx.stylize = stylizeWithColor; - return formatValue(ctx, obj, ctx.depth); + const heads = decodeHashes(decoder) + const need = decodeHashes(decoder) + const haveCount = decoder.readUint32() + let message = {heads, need, have: [], changes: []} + for (let i = 0; i < haveCount; i++) { + const lastSync = decodeHashes(decoder) + const bloom = decoder.readPrefixedBytes(decoder) + message.have.push({lastSync, bloom}) + } + const changeCount = decoder.readUint32() + for (let i = 0; i < changeCount; i++) { + const change = decoder.readPrefixedBytes() + message.changes.push(change) + } + // Ignore any trailing bytes -- they can be used for extensions by future versions of the protocol + return message } -exports.inspect = inspect; - - -// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics -inspect.colors = { - 'bold' : [1, 22], - 'italic' : [3, 23], - 'underline' : [4, 24], - 'inverse' : [7, 27], - 'white' : [37, 39], - 'grey' : [90, 39], - 'black' : [30, 39], - 'blue' : [34, 39], - 'cyan' : [36, 39], - 'green' : [32, 39], - 'magenta' : [35, 39], - 'red' : [31, 39], - 'yellow' : [33, 39] -}; - -// Don't use 'blue' not visible on cmd.exe -inspect.styles = { - 'special': 'cyan', - 'number': 'yellow', - 'boolean': 'yellow', - 'undefined': 'grey', - 'null': 'bold', - 'string': 'green', - 'date': 'magenta', - // "name": intentionally not styling - 'regexp': 'red' -}; +/** + * Takes a SyncState and encodes as a byte array those parts of the state that should persist across + * an application restart or disconnect and reconnect. The ephemeral parts of the state that should + * be cleared on reconnect are not encoded. + */ +function encodeSyncState(syncState) { + const encoder = new Encoder() + encoder.appendByte(PEER_STATE_TYPE) + encodeHashes(encoder, syncState.sharedHeads) + return encoder.buffer +} -function stylizeWithColor(str, styleType) { - var style = inspect.styles[styleType]; - - if (style) { - return '\u001b[' + inspect.colors[style][0] + 'm' + str + - '\u001b[' + inspect.colors[style][1] + 'm'; - } else { - return str; +/** + * Takes a persisted peer state as encoded by `encodeSyncState` and decodes it into a SyncState + * object. The parts of the peer state that were not encoded are initialised with default values. + */ +function decodeSyncState(bytes) { + const decoder = new Decoder(bytes) + const recordType = decoder.readByte() + if (recordType !== PEER_STATE_TYPE) { + throw new RangeError(`Unexpected record type: ${recordType}`) } + const sharedHeads = decodeHashes(decoder) + return Object.assign(initSyncState(), { sharedHeads }) } - -function stylizeNoColor(str, styleType) { - return str; +/** + * Constructs a Bloom filter containing all changes that are not one of the hashes in + * `lastSync` or its transitive dependencies. In other words, the filter contains those + * changes that have been applied since the version identified by `lastSync`. Returns + * an object of the form `{lastSync, bloom}` as required for the `have` field of a sync + * message. + */ +function makeBloomFilter(backend, lastSync) { + const newChanges = Backend.getChanges(backend, lastSync) + const hashes = newChanges.map(change => decodeChangeMeta(change, true).hash) + return {lastSync, bloom: new BloomFilter(hashes).bytes} } +/** + * Call this function when a sync message is received from another node. The `message` argument + * needs to already have been decoded using `decodeSyncMessage()`. This function determines the + * changes that we need to send to the other node in response. Returns an array of changes (as + * byte arrays). + */ +function getChangesToSend(backend, have, need) { + if (have.length === 0) { + return need.map(hash => Backend.getChangeByHash(backend, hash)).filter(change => change !== undefined) + } -function arrayToHash(array) { - var hash = {}; + let lastSyncHashes = {}, bloomFilters = [] + for (let h of have) { + for (let hash of h.lastSync) lastSyncHashes[hash] = true + bloomFilters.push(new BloomFilter(h.bloom)) + } - array.forEach(function(val, idx) { - hash[val] = true; - }); + // Get all changes that were added since the last sync + const changes = Backend.getChanges(backend, Object.keys(lastSyncHashes)) + .map(change => decodeChangeMeta(change, true)) - return hash; -} + let changeHashes = {}, dependents = {}, hashesToSend = {} + for (let change of changes) { + changeHashes[change.hash] = true + // For each change, make a list of changes that depend on it + for (let dep of change.deps) { + if (!dependents[dep]) dependents[dep] = [] + dependents[dep].push(change.hash) + } -function formatValue(ctx, value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (ctx.customInspect && - value && - isFunction(value.inspect) && - // Filter out the util module, it's inspect function is special - value.inspect !== exports.inspect && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes, ctx); - if (!isString(ret)) { - ret = formatValue(ctx, ret, recurseTimes); + // Exclude any change hashes contained in one or more Bloom filters + if (bloomFilters.every(bloom => !bloom.containsHash(change.hash))) { + hashesToSend[change.hash] = true } - return ret; } - // Primitive types cannot have properties - var primitive = formatPrimitive(ctx, value); - if (primitive) { - return primitive; + // Include any changes that depend on a Bloom-negative change + let stack = Object.keys(hashesToSend) + while (stack.length > 0) { + const hash = stack.pop() + if (dependents[hash]) { + for (let dep of dependents[hash]) { + if (!hashesToSend[dep]) { + hashesToSend[dep] = true + stack.push(dep) + } + } + } } - // Look up the keys of the object. - var keys = Object.keys(value); - var visibleKeys = arrayToHash(keys); - - if (ctx.showHidden) { - keys = Object.getOwnPropertyNames(value); + // Include any explicitly requested changes + let changesToSend = [] + for (let hash of need) { + hashesToSend[hash] = true + if (!changeHashes[hash]) { // Change is not among those returned by getMissingChanges()? + const change = Backend.getChangeByHash(backend, hash) + if (change) changesToSend.push(change) + } } - // IE doesn't make error fields non-enumerable - // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx - if (isError(value) - && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { - return formatError(value); + // Return changes in the order they were returned by getMissingChanges() + for (let change of changes) { + if (hashesToSend[change.hash]) changesToSend.push(change.change) } + return changesToSend +} - // Some type of object without properties can be shortcutted. - if (keys.length === 0) { - if (isFunction(value)) { - var name = value.name ? ': ' + value.name : ''; - return ctx.stylize('[Function' + name + ']', 'special'); - } - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } - if (isDate(value)) { - return ctx.stylize(Date.prototype.toString.call(value), 'date'); - } - if (isError(value)) { - return formatError(value); - } +function initSyncState() { + return { + sharedHeads: [], + lastSentHeads: [], + theirHeads: null, + theirNeed: null, + theirHave: null, + sentHashes: {}, } +} - var base = '', array = false, braces = ['{', '}']; - - // Make Array say that they are Array - if (isArray(value)) { - array = true; - braces = ['[', ']']; - } +function compareArrays(a, b) { + return (a.length === b.length) && a.every((v, i) => v === b[i]) +} - // Make functions say that they are functions - if (isFunction(value)) { - var n = value.name ? ': ' + value.name : ''; - base = ' [Function' + n + ']'; +/** + * Given a backend and what we believe to be the state of our peer, generate a message which tells + * them about we have and includes any changes we believe they need + */ +function generateSyncMessage(backend, syncState) { + if (!backend) { + throw new Error("generateSyncMessage called with no Automerge document") } - - // Make RegExps say that they are RegExps - if (isRegExp(value)) { - base = ' ' + RegExp.prototype.toString.call(value); + if (!syncState) { + throw new Error("generateSyncMessage requires a syncState, which can be created with initSyncState()") } - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + Date.prototype.toUTCString.call(value); - } + let { sharedHeads, lastSentHeads, theirHeads, theirNeed, theirHave, sentHashes } = syncState + const ourHeads = Backend.getHeads(backend) - // Make error with message first say the error - if (isError(value)) { - base = ' ' + formatError(value); - } + // Hashes to explicitly request from the remote peer: any missing dependencies of unapplied + // changes, and any of the remote peer's heads that we don't know about + const ourNeed = Backend.getMissingDeps(backend, theirHeads || []) - if (keys.length === 0 && (!array || value.length == 0)) { - return braces[0] + base + braces[1]; + // There are two reasons why ourNeed may be nonempty: 1. we might be missing dependencies due to + // Bloom filter false positives; 2. we might be missing heads that the other peer mentioned + // because they (intentionally) only sent us a subset of changes. In case 1, we leave the `have` + // field of the message empty because we just want to fill in the missing dependencies for now. + // In case 2, or if ourNeed is empty, we send a Bloom filter to request any unsent changes. + let ourHave = [] + if (!theirHeads || ourNeed.every(hash => theirHeads.includes(hash))) { + ourHave = [makeBloomFilter(backend, sharedHeads)] } - if (recurseTimes < 0) { - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } else { - return ctx.stylize('[Object]', 'special'); + // Fall back to a full re-sync if the sender's last sync state includes hashes + // that we don't know. This could happen if we crashed after the last sync and + // failed to persist changes that the other node already sent us. + if (theirHave && theirHave.length > 0) { + const lastSync = theirHave[0].lastSync + if (!lastSync.every(hash => Backend.getChangeByHash(backend, hash))) { + // we need to queue them to send us a fresh sync message, the one they sent is uninteligible so we don't know what they need + const resetMsg = {heads: ourHeads, need: [], have: [{ lastSync: [], bloom: new Uint8Array(0) }], changes: []} + return [syncState, encodeSyncMessage(resetMsg)] } } - ctx.seen.push(value); + // XXX: we should limit ourselves to only sending a subset of all the messages, probably limited by a total message size + // these changes should ideally be RLE encoded but we haven't implemented that yet. + let changesToSend = Array.isArray(theirHave) && Array.isArray(theirNeed) ? getChangesToSend(backend, theirHave, theirNeed) : [] - var output; - if (array) { - output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); - } else { - output = keys.map(function(key) { - return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); - }); + // If the heads are equal, we're in sync and don't need to do anything further + const headsUnchanged = Array.isArray(lastSentHeads) && compareArrays(ourHeads, lastSentHeads) + const headsEqual = Array.isArray(theirHeads) && compareArrays(ourHeads, theirHeads) + if (headsUnchanged && headsEqual && changesToSend.length === 0) { + // no need to send a sync message if we know we're synced! + return [syncState, null] } - ctx.seen.pop(); - - return reduceToSingleString(output, base, braces); -} - + // TODO: this recomputes the SHA-256 hash of each change; we should restructure this to avoid the + // unnecessary recomputation + changesToSend = changesToSend.filter(change => !sentHashes[decodeChangeMeta(change, true).hash]) -function formatPrimitive(ctx, value) { - if (isUndefined(value)) - return ctx.stylize('undefined', 'undefined'); - if (isString(value)) { - var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return ctx.stylize(simple, 'string'); + // Regular response to a sync message: send any changes that the other node + // doesn't have. We leave the "have" field empty because the previous message + // generated by `syncStart` already indicated what changes we have. + const syncMessage = {heads: ourHeads, have: ourHave, need: ourNeed, changes: changesToSend} + if (changesToSend.length > 0) { + sentHashes = copyObject(sentHashes) + for (const change of changesToSend) { + sentHashes[decodeChangeMeta(change, true).hash] = true + } } - if (isNumber(value)) - return ctx.stylize('' + value, 'number'); - if (isBoolean(value)) - return ctx.stylize('' + value, 'boolean'); - // For some reason typeof null is "object", so special case here. - if (isNull(value)) - return ctx.stylize('null', 'null'); -} + syncState = Object.assign({}, syncState, {lastSentHeads: ourHeads, sentHashes}) + return [syncState, encodeSyncMessage(syncMessage)] +} -function formatError(value) { - return '[' + Error.prototype.toString.call(value) + ']'; +/** + * Computes the heads that we share with a peer after we have just received some changes from that + * peer and applied them. This may not be sufficient to bring our heads in sync with the other + * peer's heads, since they may have only sent us a subset of their outstanding changes. + * + * `myOldHeads` are the local heads before the most recent changes were applied, `myNewHeads` are + * the local heads after those changes were applied, and `ourOldSharedHeads` is the previous set of + * shared heads. Applying the changes will have replaced some heads with others, but some heads may + * have remained unchanged (because they are for branches on which no changes have been added). Any + * such unchanged heads remain in the sharedHeads. Any sharedHeads that were replaced by applying + * changes are also replaced as sharedHeads. This is safe because if we received some changes from + * another peer, that means that peer had those changes, and therefore we now both know about them. + */ +function advanceHeads(myOldHeads, myNewHeads, ourOldSharedHeads) { + const newHeads = myNewHeads.filter((head) => !myOldHeads.includes(head)) + const commonHeads = ourOldSharedHeads.filter((head) => myNewHeads.includes(head)) + const advancedHeads = [...new Set([...newHeads, ...commonHeads])].sort() + return advancedHeads } -function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - for (var i = 0, l = value.length; i < l; ++i) { - if (hasOwnProperty(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); - } +/** + * Given a backend, a message message and the state of our peer, apply any changes, update what + * we believe about the peer, and (if there were applied changes) produce a patch for the frontend + */ +function receiveSyncMessage(backend, oldSyncState, binaryMessage) { + if (!backend) { + throw new Error("generateSyncMessage called with no Automerge document") + } + if (!oldSyncState) { + throw new Error("generateSyncMessage requires a syncState, which can be created with initSyncState()") } - keys.forEach(function(key) { - if (!key.match(/^\d+$/)) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, true)); - } - }); - return output; -} + let { sharedHeads, lastSentHeads, sentHashes } = oldSyncState, patch = null + const message = decodeSyncMessage(binaryMessage) + const beforeHeads = Backend.getHeads(backend) -function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str, desc; - desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; - if (desc.get) { - if (desc.set) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } - } else { - if (desc.set) { - str = ctx.stylize('[Setter]', 'special'); - } - } - if (!hasOwnProperty(visibleKeys, key)) { - name = '[' + key + ']'; + // If we received changes, we try to apply them to the document. There may still be missing + // dependencies due to Bloom filter false positives, in which case the backend will enqueue the + // changes without applying them. The set of changes may also be incomplete if the sender decided + // to break a large set of changes into chunks. + if (message.changes.length > 0) { + [backend, patch] = Backend.applyChanges(backend, message.changes) + sharedHeads = advanceHeads(beforeHeads, Backend.getHeads(backend), sharedHeads) } - if (!str) { - if (ctx.seen.indexOf(desc.value) < 0) { - if (isNull(recurseTimes)) { - str = formatValue(ctx, desc.value, null); - } else { - str = formatValue(ctx, desc.value, recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (array) { - str = str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n').substr(2); - } else { - str = '\n' + str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n'); - } - } - } else { - str = ctx.stylize('[Circular]', 'special'); - } + + // If heads are equal, indicate we don't need to send a response message + if (message.changes.length === 0 && compareArrays(message.heads, beforeHeads)) { + lastSentHeads = message.heads } - if (isUndefined(name)) { - if (array && key.match(/^\d+$/)) { - return str; - } - name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.substr(1, name.length - 2); - name = ctx.stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = ctx.stylize(name, 'string'); + + // If all of the remote heads are known to us, that means either our heads are equal, or we are + // ahead of the remote peer. In this case, take the remote heads to be our shared heads. + const knownHeads = message.heads.filter(head => Backend.getChangeByHash(backend, head)) + if (knownHeads.length === message.heads.length) { + sharedHeads = message.heads + // If the remote peer has lost all its data, reset our state to perform a full resync + if (message.heads.length === 0) { + lastSentHeads = [] + sentHashes = [] } + } else { + // If some remote heads are unknown to us, we add all the remote heads we know to + // sharedHeads, but don't remove anything from sharedHeads. This might cause sharedHeads to + // contain some redundant hashes (where one hash is actually a transitive dependency of + // another), but this will be cleared up as soon as we know all the remote heads. + sharedHeads = [...new Set(knownHeads.concat(sharedHeads))].sort() } - return name + ': ' + str; + const syncState = { + sharedHeads, // what we have in common to generate an efficient bloom filter + lastSentHeads, + theirHave: message.have, // the information we need to calculate the changes they need + theirHeads: message.heads, + theirNeed: message.need, + sentHashes + } + return [backend, syncState, patch] } +module.exports = { + receiveSyncMessage, generateSyncMessage, + encodeSyncMessage, decodeSyncMessage, + initSyncState, encodeSyncState, decodeSyncState, + BloomFilter // BloomFilter is a private API, exported only for testing purposes +} -function reduceToSingleString(output, base, braces) { - var numLinesEst = 0; - var length = output.reduce(function(prev, cur) { - numLinesEst++; - if (cur.indexOf('\n') >= 0) numLinesEst++; - return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; - }, 0); - - if (length > 60) { - return braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; +},{"../src/common":101,"./backend":83,"./columnar":84,"./encoding":85}],89:[function(require,module,exports){ +function backendState(backend) { + if (backend.frozen) { + throw new Error( + 'Attempting to use an outdated Automerge document that has already been updated. ' + + 'Please use the latest document state, or call Automerge.clone() if you really ' + + 'need to use this old document state.' + ) } - - return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; + return backend.state } +module.exports = { + backendState +} -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. -exports.types = require('./support/types'); +},{}],90:[function(require,module,exports){ +const { isObject, copyObject, parseOpId } = require('../src/common') +const { OBJECT_ID, CONFLICTS, ELEM_IDS } = require('./constants') +const { instantiateText } = require('./text') +const { instantiateTable } = require('./table') +const { Counter } = require('./counter') -function isArray(ar) { - return Array.isArray(ar); +/** + * Reconstructs the value from the patch object `patch`. + */ +function getValue(patch, object, updated) { + if (patch.objectId) { + // If the objectId of the existing object does not match the objectId in the patch, + // that means the patch is replacing the object with a new one made from scratch + if (object && object[OBJECT_ID] !== patch.objectId) { + object = undefined + } + return interpretPatch(patch, object, updated) + } else if (patch.datatype === 'timestamp') { + // Timestamp: value is milliseconds since 1970 epoch + return new Date(patch.value) + } else if (patch.datatype === 'counter') { + return new Counter(patch.value) + } else { + // Primitive value (int, uint, float64, string, boolean, or null) + return patch.value + } } -exports.isArray = isArray; -function isBoolean(arg) { - return typeof arg === 'boolean'; +/** + * Compares two strings, interpreted as Lamport timestamps of the form + * 'counter@actorId'. Returns 1 if ts1 is greater, or -1 if ts2 is greater. + */ +function lamportCompare(ts1, ts2) { + const regex = /^(\d+)@(.*)$/ + const time1 = regex.test(ts1) ? parseOpId(ts1) : {counter: 0, actorId: ts1} + const time2 = regex.test(ts2) ? parseOpId(ts2) : {counter: 0, actorId: ts2} + if (time1.counter < time2.counter) return -1 + if (time1.counter > time2.counter) return 1 + if (time1.actorId < time2.actorId) return -1 + if (time1.actorId > time2.actorId) return 1 + return 0 } -exports.isBoolean = isBoolean; -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; +/** + * `props` is an object of the form: + * `{key1: {opId1: {...}, opId2: {...}}, key2: {opId3: {...}}}` + * where the outer object is a mapping from property names to inner objects, + * and the inner objects are a mapping from operation ID to sub-patch. + * This function interprets that structure and updates the objects `object` and + * `conflicts` to reflect it. For each key, the greatest opId (by Lamport TS + * order) is chosen as the default resolution; that op's value is assigned + * to `object[key]`. Moreover, all the opIds and values are packed into a + * conflicts object of the form `{opId1: value1, opId2: value2}` and assigned + * to `conflicts[key]`. If there is no conflict, the conflicts object contains + * just a single opId-value mapping. + */ +function applyProperties(props, object, conflicts, updated) { + if (!props) return -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; + for (let key of Object.keys(props)) { + const values = {}, opIds = Object.keys(props[key]).sort(lamportCompare).reverse() + for (let opId of opIds) { + const subpatch = props[key][opId] + if (conflicts[key] && conflicts[key][opId]) { + values[opId] = getValue(subpatch, conflicts[key][opId], updated) + } else { + values[opId] = getValue(subpatch, undefined, updated) + } + } -function isNumber(arg) { - return typeof arg === 'number'; + if (opIds.length === 0) { + delete object[key] + delete conflicts[key] + } else { + object[key] = values[opIds[0]] + conflicts[key] = values + } + } } -exports.isNumber = isNumber; -function isString(arg) { - return typeof arg === 'string'; +/** + * Creates a writable copy of an immutable map object. If `originalObject` + * is undefined, creates an empty object with ID `objectId`. + */ +function cloneMapObject(originalObject, objectId) { + const object = copyObject(originalObject) + const conflicts = copyObject(originalObject ? originalObject[CONFLICTS] : undefined) + Object.defineProperty(object, OBJECT_ID, {value: objectId}) + Object.defineProperty(object, CONFLICTS, {value: conflicts}) + return object } -exports.isString = isString; -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; +/** + * Updates the map object `obj` according to the modifications described in + * `patch`, or creates a new object if `obj` is undefined. Mutates `updated` + * to map the objectId to the new object, and returns the new object. + */ +function updateMapObject(patch, obj, updated) { + const objectId = patch.objectId + if (!updated[objectId]) { + updated[objectId] = cloneMapObject(obj, objectId) + } -function isUndefined(arg) { - return arg === void 0; + const object = updated[objectId] + applyProperties(patch.props, object, object[CONFLICTS], updated) + return object } -exports.isUndefined = isUndefined; -function isRegExp(re) { - return isObject(re) && objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; -exports.types.isRegExp = isRegExp; +/** + * Updates the table object `obj` according to the modifications described in + * `patch`, or creates a new object if `obj` is undefined. Mutates `updated` + * to map the objectId to the new object, and returns the new object. + */ +function updateTableObject(patch, obj, updated) { + const objectId = patch.objectId + if (!updated[objectId]) { + updated[objectId] = obj ? obj._clone() : instantiateTable(objectId) + } -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; + const object = updated[objectId] -function isDate(d) { - return isObject(d) && objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; -exports.types.isDate = isDate; + for (let key of Object.keys(patch.props || {})) { + const opIds = Object.keys(patch.props[key]) -function isError(e) { - return isObject(e) && - (objectToString(e) === '[object Error]' || e instanceof Error); + if (opIds.length === 0) { + object.remove(key) + } else if (opIds.length === 1) { + const subpatch = patch.props[key][opIds[0]] + object._set(key, getValue(subpatch, object.byId(key), updated), opIds[0]) + } else { + throw new RangeError('Conflicts are not supported on properties of a table') + } + } + return object } -exports.isError = isError; -exports.types.isNativeError = isError; -function isFunction(arg) { - return typeof arg === 'function'; +/** + * Creates a writable copy of an immutable list object. If `originalList` is + * undefined, creates an empty list with ID `objectId`. + */ +function cloneListObject(originalList, objectId) { + const list = originalList ? originalList.slice() : [] // slice() makes a shallow clone + const conflicts = (originalList && originalList[CONFLICTS]) ? originalList[CONFLICTS].slice() : [] + const elemIds = (originalList && originalList[ELEM_IDS]) ? originalList[ELEM_IDS].slice() : [] + Object.defineProperty(list, OBJECT_ID, {value: objectId}) + Object.defineProperty(list, CONFLICTS, {value: conflicts}) + Object.defineProperty(list, ELEM_IDS, {value: elemIds}) + return list } -exports.isFunction = isFunction; -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; +/** + * Updates the list object `obj` according to the modifications described in + * `patch`, or creates a new object if `obj` is undefined. Mutates `updated` + * to map the objectId to the new object, and returns the new object. + */ +function updateListObject(patch, obj, updated) { + const objectId = patch.objectId + if (!updated[objectId]) { + updated[objectId] = cloneListObject(obj, objectId) + } -exports.isBuffer = require('./support/isBuffer'); + const list = updated[objectId], conflicts = list[CONFLICTS], elemIds = list[ELEM_IDS] + for (let i = 0; i < patch.edits.length; i++) { + const edit = patch.edits[i] -function objectToString(o) { - return Object.prototype.toString.call(o); -} + if (edit.action === 'insert' || edit.action === 'update') { + const oldValue = conflicts[edit.index] && conflicts[edit.index][edit.opId] + let lastValue = getValue(edit.value, oldValue, updated) + let values = {[edit.opId]: lastValue} + + // Successive updates for the same index are an indication of a conflict on that list element. + // Edits are sorted in increasing order by Lamport timestamp, so the last value (with the + // greatest timestamp) is the default resolution of the conflict. + while (i < patch.edits.length - 1 && patch.edits[i + 1].index === edit.index && + patch.edits[i + 1].action === 'update') { + i++ + const conflict = patch.edits[i] + const oldValue2 = conflicts[conflict.index] && conflicts[conflict.index][conflict.opId] + lastValue = getValue(conflict.value, oldValue2, updated) + values[conflict.opId] = lastValue + } + if (edit.action === 'insert') { + list.splice(edit.index, 0, lastValue) + conflicts.splice(edit.index, 0, values) + elemIds.splice(edit.index, 0, edit.elemId) + } else { + list[edit.index] = lastValue + conflicts[edit.index] = values + } -function pad(n) { - return n < 10 ? '0' + n.toString(10) : n.toString(10); + } else if (edit.action === 'multi-insert') { + const startElemId = parseOpId(edit.elemId), newElems = [], newValues = [], newConflicts = [] + const datatype = edit.datatype + edit.values.forEach((value, index) => { + const elemId = `${startElemId.counter + index}@${startElemId.actorId}` + value = getValue({ value, datatype }, undefined, updated) + newValues.push(value) + newConflicts.push({[elemId]: {value, datatype, type: 'value'}}) + newElems.push(elemId) + }) + list.splice(edit.index, 0, ...newValues) + conflicts.splice(edit.index, 0, ...newConflicts) + elemIds.splice(edit.index, 0, ...newElems) + + } else if (edit.action === 'remove') { + list.splice(edit.index, edit.count) + conflicts.splice(edit.index, edit.count) + elemIds.splice(edit.index, edit.count) + } + } + return list } +/** + * Updates the text object `obj` according to the modifications described in + * `patch`, or creates a new object if `obj` is undefined. Mutates `updated` + * to map the objectId to the new object, and returns the new object. + */ +function updateTextObject(patch, obj, updated) { + const objectId = patch.objectId + let elems + if (updated[objectId]) { + elems = updated[objectId].elems + } else if (obj) { + elems = obj.elems.slice() + } else { + elems = [] + } -var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; + for (const edit of patch.edits) { + if (edit.action === 'insert') { + const value = getValue(edit.value, undefined, updated) + const elem = {elemId: edit.elemId, pred: [edit.opId], value} + elems.splice(edit.index, 0, elem) -// 26 Feb 16:19:34 -function timestamp() { - var d = new Date(); - var time = [pad(d.getHours()), - pad(d.getMinutes()), - pad(d.getSeconds())].join(':'); - return [d.getDate(), months[d.getMonth()], time].join(' '); -} + } else if (edit.action === 'multi-insert') { + const startElemId = parseOpId(edit.elemId) + const datatype = edit.datatype + const newElems = edit.values.map((value, index) => { + value = getValue({ datatype, value }, undefined, updated) + const elemId = `${startElemId.counter + index}@${startElemId.actorId}` + return {elemId, pred: [elemId], value} + }) + elems.splice(edit.index, 0, ...newElems) + } else if (edit.action === 'update') { + const elemId = elems[edit.index].elemId + const value = getValue(edit.value, elems[edit.index].value, updated) + elems[edit.index] = {elemId, pred: [edit.opId], value} -// log is just a thin wrapper to console.log that prepends a timestamp -exports.log = function() { - console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); -}; + } else if (edit.action === 'remove') { + elems.splice(edit.index, edit.count) + } + } + updated[objectId] = instantiateText(objectId, elems) + return updated[objectId] +} /** - * Inherit the prototype methods from one constructor into another. - * - * The Function.prototype.inherits from lang.js rewritten as a standalone - * function (not on Function.prototype). NOTE: If this file is to be loaded - * during bootstrapping this function needs to be rewritten using some native - * functions as prototype setup using normal JavaScript does not work as - * expected during bootstrapping (see mirror.js in r114903). - * - * @param {function} ctor Constructor function which needs to inherit the - * prototype. - * @param {function} superCtor Constructor function to inherit prototype from. + * Applies the patch object `patch` to the read-only document object `obj`. + * Clones a writable copy of `obj` and places it in `updated` (indexed by + * objectId), if that has not already been done. Returns the updated object. */ -exports.inherits = require('inherits'); - -exports._extend = function(origin, add) { - // Don't do anything if add isn't an object - if (!add || !isObject(add)) return origin; +function interpretPatch(patch, obj, updated) { + // Return original object if it already exists and isn't being modified + if (isObject(obj) && (!patch.props || Object.keys(patch.props).length === 0) && + (!patch.edits || patch.edits.length === 0) && !updated[patch.objectId]) { + return obj + } - var keys = Object.keys(add); - var i = keys.length; - while (i--) { - origin[keys[i]] = add[keys[i]]; + if (patch.type === 'map') { + return updateMapObject(patch, obj, updated) + } else if (patch.type === 'table') { + return updateTableObject(patch, obj, updated) + } else if (patch.type === 'list') { + return updateListObject(patch, obj, updated) + } else if (patch.type === 'text') { + return updateTextObject(patch, obj, updated) + } else { + throw new TypeError(`Unknown object type: ${patch.type}`) } - return origin; -}; +} -function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); +/** + * Creates a writable copy of the immutable document root object `root`. + */ +function cloneRootObject(root) { + if (root[OBJECT_ID] !== '_root') { + throw new RangeError(`Not the root object: ${root[OBJECT_ID]}`) + } + return cloneMapObject(root, '_root') } -var kCustomPromisifiedSymbol = typeof Symbol !== 'undefined' ? Symbol('util.promisify.custom') : undefined; +module.exports = { + interpretPatch, cloneRootObject +} -exports.promisify = function promisify(original) { - if (typeof original !== 'function') - throw new TypeError('The "original" argument must be of type Function'); +},{"../src/common":101,"./constants":91,"./counter":93,"./table":98,"./text":99}],91:[function(require,module,exports){ +// Properties of the document root object +const OPTIONS = Symbol('_options') // object containing options passed to init() +const CACHE = Symbol('_cache') // map from objectId to immutable object +const STATE = Symbol('_state') // object containing metadata about current state (e.g. sequence numbers) - if (kCustomPromisifiedSymbol && original[kCustomPromisifiedSymbol]) { - var fn = original[kCustomPromisifiedSymbol]; - if (typeof fn !== 'function') { - throw new TypeError('The "util.promisify.custom" argument must be of type Function'); - } - Object.defineProperty(fn, kCustomPromisifiedSymbol, { - value: fn, enumerable: false, writable: false, configurable: true - }); - return fn; - } +// Properties of all Automerge objects +const OBJECT_ID = Symbol('_objectId') // the object ID of the current object (string) +const CONFLICTS = Symbol('_conflicts') // map or list (depending on object type) of conflicts +const CHANGE = Symbol('_change') // the context object on proxy objects used in change callback +const ELEM_IDS = Symbol('_elemIds') // list containing the element ID of each list element - function fn() { - var promiseResolve, promiseReject; - var promise = new Promise(function (resolve, reject) { - promiseResolve = resolve; - promiseReject = reject; - }); +module.exports = { + OPTIONS, CACHE, STATE, OBJECT_ID, CONFLICTS, CHANGE, ELEM_IDS +} - var args = []; - for (var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); - } - args.push(function (err, value) { - if (err) { - promiseReject(err); - } else { - promiseResolve(value); - } - }); +},{}],92:[function(require,module,exports){ +const { CACHE, OBJECT_ID, CONFLICTS, ELEM_IDS, STATE } = require('./constants') +const { interpretPatch } = require('./apply_patch') +const { Text } = require('./text') +const { Table } = require('./table') +const { Counter, getWriteableCounter } = require('./counter') +const { Int, Uint, Float64 } = require('./numbers') +const { isObject, parseOpId, createArrayOfNulls } = require('../src/common') +const uuid = require('../src/uuid') - try { - original.apply(this, args); - } catch (err) { - promiseReject(err); - } - return promise; +/** + * An instance of this class is passed to `rootObjectProxy()`. The methods are + * called by proxy object mutation functions to query the current object state + * and to apply the requested changes. + */ +class Context { + constructor (doc, actorId, applyPatch) { + this.actorId = actorId + this.nextOpNum = doc[STATE].maxOp + 1 + this.cache = doc[CACHE] + this.updated = {} + this.ops = [] + this.applyPatch = applyPatch ? applyPatch : interpretPatch } - Object.setPrototypeOf(fn, Object.getPrototypeOf(original)); - - if (kCustomPromisifiedSymbol) Object.defineProperty(fn, kCustomPromisifiedSymbol, { - value: fn, enumerable: false, writable: false, configurable: true - }); - return Object.defineProperties( - fn, - getOwnPropertyDescriptors(original) - ); -} - -exports.promisify.custom = kCustomPromisifiedSymbol + /** + * Adds an operation object to the list of changes made in the current context. + */ + addOp(operation) { + this.ops.push(operation) -function callbackifyOnRejected(reason, cb) { - // `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M). - // Because `null` is a special error value in callbacks which means "no error - // occurred", we error-wrap so the callback consumer can distinguish between - // "the promise rejected with null" or "the promise fulfilled with undefined". - if (!reason) { - var newReason = new Error('Promise was rejected with a falsy value'); - newReason.reason = reason; - reason = newReason; + if (operation.action === 'set' && operation.values) { + this.nextOpNum += operation.values.length + } else if (operation.action === 'del' && operation.multiOp) { + this.nextOpNum += operation.multiOp + } else { + this.nextOpNum += 1 + } } - return cb(reason); -} -function callbackify(original) { - if (typeof original !== 'function') { - throw new TypeError('The "original" argument must be of type Function'); + /** + * Returns the operation ID of the next operation to be added to the context. + */ + nextOpId() { + return `${this.nextOpNum}@${this.actorId}` } - // We DO NOT return the promise as it gives the user a false sense that - // the promise is actually somehow related to the callback's execution - // and that the callback throwing will reject the promise. - function callbackified() { - var args = []; - for (var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); + /** + * Takes a value and returns an object describing the value (in the format used by patches). + */ + getValueDescription(value) { + if (!['object', 'boolean', 'number', 'string'].includes(typeof value)) { + throw new TypeError(`Unsupported type of value: ${typeof value}`) } - var maybeCb = args.pop(); - if (typeof maybeCb !== 'function') { - throw new TypeError('The last argument must be of type Function'); - } - var self = this; - var cb = function() { - return maybeCb.apply(self, arguments); - }; - // In true node style we process the callback on `nextTick` with all the - // implications (stack, `uncaughtException`, `async_hooks`) - original.apply(this, args) - .then(function(ret) { process.nextTick(cb.bind(null, null, ret)) }, - function(rej) { process.nextTick(callbackifyOnRejected.bind(null, rej, cb)) }); - } + if (isObject(value)) { + if (value instanceof Date) { + // Date object, represented as milliseconds since epoch + return {type: 'value', value: value.getTime(), datatype: 'timestamp'} - Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original)); - Object.defineProperties(callbackified, - getOwnPropertyDescriptors(original)); - return callbackified; -} -exports.callbackify = callbackify; + } else if (value instanceof Int) { + return {type: 'value', value: value.value, datatype: 'int'} + } else if (value instanceof Uint) { + return {type: 'value', value: value.value, datatype: 'uint'} + } else if (value instanceof Float64) { + return {type: 'value', value: value.value, datatype: 'float64'} + } else if (value instanceof Counter) { + // Counter object + return {type: 'value', value: value.value, datatype: 'counter'} -}).call(this)}).call(this,require('_process')) -},{"./support/isBuffer":151,"./support/types":152,"_process":377,"inherits":243}],154:[function(require,module,exports){ -(function (Buffer){(function (){ -module.exports = function xor (a, b) { - var length = Math.min(a.length, b.length) - var buffer = new Buffer(length) + } else { + // Nested object (map, list, text, or table) + const objectId = value[OBJECT_ID], type = this.getObjectType(objectId) + if (!objectId) { + throw new RangeError(`Object ${JSON.stringify(value)} has no objectId`) + } + if (type === 'list' || type === 'text') { + return {objectId, type, edits: []} + } else { + return {objectId, type, props: {}} + } + } + } else if (typeof value === 'number') { + if (Number.isInteger(value) && value <= Number.MAX_SAFE_INTEGER && value >= Number.MIN_SAFE_INTEGER) { + return {type: 'value', value, datatype: 'int'} + } else { + return {type: 'value', value, datatype: 'float64'} + } + } else { + // Primitive value (string, boolean, or null) + return {type: 'value', value} + } + } - for (var i = 0; i < length; ++i) { - buffer[i] = a[i] ^ b[i] + /** + * Builds the values structure describing a single property in a patch. Finds all the values of + * property `key` of `object` (there might be multiple values in the case of a conflict), and + * returns an object that maps operation IDs to descriptions of values. + */ + getValuesDescriptions(path, object, key) { + if (object instanceof Table) { + // Table objects don't have conflicts, since rows are identified by their unique objectId + const value = object.byId(key) + return value ? {[key]: this.getValueDescription(value)} : {} + } else if (object instanceof Text) { + // Text objects don't support conflicts + const value = object.get(key) + const elemId = object.getElemId(key) + return value ? {[elemId]: this.getValueDescription(value)} : {} + } else { + // Map or list objects + const conflicts = object[CONFLICTS][key], values = {} + if (!conflicts) { + throw new RangeError(`No children at key ${key} of path ${JSON.stringify(path)}`) + } + for (let opId of Object.keys(conflicts)) { + values[opId] = this.getValueDescription(conflicts[opId]) + } + return values + } } - return buffer -} + /** + * Returns the value at property `key` of object `object`. In the case of a conflict, returns + * the value whose assignment operation has the ID `opId`. + */ + getPropertyValue(object, key, opId) { + if (object instanceof Table) { + return object.byId(key) + } else if (object instanceof Text) { + return object.get(key) + } else { + return object[CONFLICTS][key][opId] + } + } -}).call(this)}).call(this,require("buffer").Buffer) -},{"buffer":135}],155:[function(require,module,exports){ -'use strict'; + /** + * Recurses along `path` into the patch object `patch`, creating nodes along the way as needed + * by mutating the patch object. Returns the subpatch at the given path. + */ + getSubpatch(patch, path) { + if (path.length == 0) return patch + let subpatch = patch, object = this.getObject('_root') -var GetIntrinsic = require('get-intrinsic'); + for (let pathElem of path) { + let values = this.getValuesDescriptions(path, object, pathElem.key) + if (subpatch.props) { + if (!subpatch.props[pathElem.key]) { + subpatch.props[pathElem.key] = values + } + } else if (subpatch.edits) { + for (const opId of Object.keys(values)) { + subpatch.edits.push({action: 'update', index: pathElem.key, opId, value: values[opId]}) + } + } -var callBind = require('./'); + let nextOpId = null + for (let opId of Object.keys(values)) { + if (values[opId].objectId === pathElem.objectId) { + nextOpId = opId + } + } + if (!nextOpId) { + throw new RangeError(`Cannot find path object with objectId ${pathElem.objectId}`) + } -var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf')); + subpatch = values[nextOpId] + object = this.getPropertyValue(object, pathElem.key, nextOpId) + } -module.exports = function callBoundIntrinsic(name, allowMissing) { - var intrinsic = GetIntrinsic(name, !!allowMissing); - if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) { - return callBind(intrinsic); - } - return intrinsic; -}; + return subpatch + } -},{"./":156,"get-intrinsic":207}],156:[function(require,module,exports){ -'use strict'; + /** + * Returns an object (not proxied) from the cache or updated set, as appropriate. + */ + getObject(objectId) { + const object = this.updated[objectId] || this.cache[objectId] + if (!object) throw new RangeError(`Target object does not exist: ${objectId}`) + return object + } -var bind = require('function-bind'); -var GetIntrinsic = require('get-intrinsic'); + /** + * Returns a string that is either 'map', 'table', 'list', or 'text', indicating + * the type of the object with ID `objectId`. + */ + getObjectType(objectId) { + if (objectId === '_root') return 'map' + const object = this.getObject(objectId) + if (object instanceof Text) return 'text' + if (object instanceof Table) return 'table' + if (Array.isArray(object)) return 'list' + return 'map' + } -var $apply = GetIntrinsic('%Function.prototype.apply%'); -var $call = GetIntrinsic('%Function.prototype.call%'); -var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply); + /** + * Returns the value associated with the property named `key` on the object + * at path `path`. If the value is an object, returns a proxy for it. + */ + getObjectField(path, objectId, key) { + if (!['string', 'number'].includes(typeof key)) return + const object = this.getObject(objectId) -var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true); -var $defineProperty = GetIntrinsic('%Object.defineProperty%', true); -var $max = GetIntrinsic('%Math.max%'); + if (object[key] instanceof Counter) { + return getWriteableCounter(object[key].value, this, path, objectId, key) -if ($defineProperty) { - try { - $defineProperty({}, 'a', { value: 1 }); - } catch (e) { - // IE 8 has a broken defineProperty - $defineProperty = null; - } -} + } else if (isObject(object[key])) { + const childId = object[key][OBJECT_ID] + const subpath = path.concat([{key, objectId: childId}]) + // The instantiateObject function is added to the context object by rootObjectProxy() + return this.instantiateObject(subpath, childId) -module.exports = function callBind(originalFunction) { - var func = $reflectApply(bind, $call, arguments); - if ($gOPD && $defineProperty) { - var desc = $gOPD(func, 'length'); - if (desc.configurable) { - // original length, plus the receiver, minus any additional arguments (after the receiver) - $defineProperty( - func, - 'length', - { value: 1 + $max(0, originalFunction.length - (arguments.length - 1)) } - ); - } - } - return func; -}; + } else { + return object[key] + } + } -var applyBind = function applyBind() { - return $reflectApply(bind, $apply, arguments); -}; + /** + * Recursively creates Automerge versions of all the objects and nested objects in `value`, + * constructing a patch and operations that describe the object tree. The new object is + * assigned to the property `key` in the object with ID `obj`. If the object is a list or + * text, `key` must be set to the list index being updated, and `elemId` must be set to the + * elemId of the element being updated. If `insert` is true, we insert a new list element + * (or text character) at index `key`, and `elemId` must be the elemId of the immediate + * predecessor element (or the string '_head' if inserting at index 0). If the assignment + * overwrites a previous value at this key/element, `pred` must be set to the array of the + * prior operations we are overwriting (empty array if there is no existing value). + */ + createNestedObjects(obj, key, value, insert, pred, elemId) { + if (value[OBJECT_ID]) { + throw new RangeError('Cannot create a reference to an existing document object') + } + const objectId = this.nextOpId() -if ($defineProperty) { - $defineProperty(module.exports, 'apply', { value: applyBind }); -} else { - module.exports.apply = applyBind; -} + if (value instanceof Text) { + // Create a new Text object + this.addOp(elemId ? {action: 'makeText', obj, elemId, insert, pred} + : {action: 'makeText', obj, key, insert, pred}) + const subpatch = {objectId, type: 'text', edits: []} + this.insertListItems(subpatch, 0, [...value], true) + return subpatch -},{"function-bind":206,"get-intrinsic":207}],157:[function(require,module,exports){ -'use strict' + } else if (value instanceof Table) { + // Create a new Table object + if (value.count > 0) { + throw new RangeError('Assigning a non-empty Table object is not supported') + } + this.addOp(elemId ? {action: 'makeTable', obj, elemId, insert, pred} + : {action: 'makeTable', obj, key, insert, pred}) + return {objectId, type: 'table', props: {}} -var nextTick = require('./next-tick') + } else if (Array.isArray(value)) { + // Create a new list object + this.addOp(elemId ? {action: 'makeList', obj, elemId, insert, pred} + : {action: 'makeList', obj, key, insert, pred}) + const subpatch = {objectId, type: 'list', edits: []} + this.insertListItems(subpatch, 0, value, true) + return subpatch -exports.fromCallback = function (callback, symbol) { - if (callback === undefined) { - var promise = new Promise(function (resolve, reject) { - callback = function (err, res) { - if (err) reject(err) - else resolve(res) + } else { + // Create a new map object + this.addOp(elemId ? {action: 'makeMap', obj, elemId, insert, pred} + : {action: 'makeMap', obj, key, insert, pred}) + let props = {} + for (let nested of Object.keys(value).sort()) { + const opId = this.nextOpId() + const valuePatch = this.setValue(objectId, nested, value[nested], false, []) + props[nested] = {[opId]: valuePatch} } - }) - - callback[symbol !== undefined ? symbol : 'promise'] = promise - } else if (typeof callback !== 'function') { - throw new TypeError('Callback must be a function') + return {objectId, type: 'map', props} + } } - return callback -} - -exports.fromPromise = function (promise, callback) { - if (callback === undefined) return promise - - promise - .then(function (res) { nextTick(() => callback(null, res)) }) - .catch(function (err) { nextTick(() => callback(err)) }) -} - -},{"./next-tick":158}],158:[function(require,module,exports){ -module.exports = typeof queueMicrotask === 'function' ? queueMicrotask : (fn) => Promise.resolve().then(fn) - -},{}],159:[function(require,module,exports){ -var Buffer = require('safe-buffer').Buffer -var Transform = require('stream').Transform -var StringDecoder = require('string_decoder').StringDecoder -var inherits = require('inherits') + /** + * Records an assignment to a particular key in a map, or a particular index in a list. + * `objectId` is the ID of the object being modified, `key` is the property name or list + * index being updated, and `value` is the new value being assigned. If `insert` is true, + * a new list element is inserted at index `key`, and `value` is assigned to that new list + * element. `pred` is an array of opIds for previous values of the property being assigned, + * which are overwritten by this operation. If the object being modified is a list or text, + * `elemId` is the element ID of the list element being updated (if insert=false), or the + * element ID of the list element immediately preceding the insertion (if insert=true). + * + * Returns a patch describing the new value. The return value is of the form + * `{objectId, type, props}` if `value` is an object, or `{value, datatype}` if it is a + * primitive value. For string, number, boolean, or null the datatype is omitted. + */ + setValue(objectId, key, value, insert, pred, elemId) { + if (!objectId) { + throw new RangeError('setValue needs an objectId') + } + if (key === '') { + throw new RangeError('The key of a map entry must not be an empty string') + } -function CipherBase (hashMode) { - Transform.call(this) - this.hashMode = typeof hashMode === 'string' - if (this.hashMode) { - this[hashMode] = this._finalOrDigest - } else { - this.final = this._finalOrDigest - } - if (this._final) { - this.__final = this._final - this._final = null + if (isObject(value) && !(value instanceof Date) && !(value instanceof Counter) && !(value instanceof Int) && !(value instanceof Uint) && !(value instanceof Float64)) { + // Nested object (map, list, text, or table) + return this.createNestedObjects(objectId, key, value, insert, pred, elemId) + } else { + // Date or counter object, or primitive value (number, string, boolean, or null) + const description = this.getValueDescription(value) + const op = {action: 'set', obj: objectId, insert, value: description.value, pred} + if (elemId) op.elemId = elemId; else op.key = key + if (description.datatype) op.datatype = description.datatype + this.addOp(op) + return description + } } - this._decoder = null - this._encoding = null -} -inherits(CipherBase, Transform) -CipherBase.prototype.update = function (data, inputEnc, outputEnc) { - if (typeof data === 'string') { - data = Buffer.from(data, inputEnc) + /** + * Constructs a new patch, calls `callback` with the subpatch at the location `path`, + * and then immediately applies the patch to the document. + */ + applyAtPath(path, callback) { + let diff = {objectId: '_root', type: 'map', props: {}} + callback(this.getSubpatch(diff, path)) + this.applyPatch(diff, this.cache._root, this.updated) } - var outData = this._update(data) - if (this.hashMode) return this + /** + * Updates the map object at path `path`, setting the property with name + * `key` to `value`. + */ + setMapKey(path, key, value) { + if (typeof key !== 'string') { + throw new RangeError(`The key of a map entry must be a string, not ${typeof key}`) + } - if (outputEnc) { - outData = this._toString(outData, outputEnc) + const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId + const object = this.getObject(objectId) + if (object[key] instanceof Counter) { + throw new RangeError('Cannot overwrite a Counter object; use .increment() or .decrement() to change its value.') + } + + // If the assigned field value is the same as the existing value, and + // the assignment does not resolve a conflict, do nothing + if (object[key] !== value || Object.keys(object[CONFLICTS][key] || {}).length > 1 || value === undefined) { + this.applyAtPath(path, subpatch => { + const pred = getPred(object, key) + const opId = this.nextOpId() + const valuePatch = this.setValue(objectId, key, value, false, pred) + subpatch.props[key] = {[opId]: valuePatch} + }) + } } - return outData -} + /** + * Updates the map object at path `path`, deleting the property `key`. + */ + deleteMapKey(path, key) { + const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId + const object = this.getObject(objectId) -CipherBase.prototype.setAutoPadding = function () {} -CipherBase.prototype.getAuthTag = function () { - throw new Error('trying to get auth tag in unsupported state') -} + if (object[key] !== undefined) { + const pred = getPred(object, key) + this.addOp({action: 'del', obj: objectId, key, insert: false, pred}) + this.applyAtPath(path, subpatch => { + subpatch.props[key] = {} + }) + } + } -CipherBase.prototype.setAuthTag = function () { - throw new Error('trying to set auth tag in unsupported state') -} + /** + * Inserts a sequence of new list elements `values` into a list, starting at position `index`. + * `newObject` is true if we are creating a new list object, and false if we are updating an + * existing one. `subpatch` is the patch for the list object being modified. Mutates + * `subpatch` to reflect the sequence of values. + */ + insertListItems(subpatch, index, values, newObject) { + const list = newObject ? [] : this.getObject(subpatch.objectId) + if (index < 0 || index > list.length) { + throw new RangeError(`List index ${index} is out of bounds for list of length ${list.length}`) + } + if (values.length === 0) return -CipherBase.prototype.setAAD = function () { - throw new Error('trying to set aad in unsupported state') -} + let elemId = getElemId(list, index, true) + const allPrimitive = values.every(v => typeof v === 'string' || typeof v === 'number' || + typeof v === 'boolean' || v === null || + (isObject(v) && (v instanceof Date || v instanceof Counter || v instanceof Int || + v instanceof Uint || v instanceof Float64))) + const allValueDescriptions = allPrimitive ? values.map(v => this.getValueDescription(v)) : [] + const allDatatypesSame = allValueDescriptions.every(t => t.datatype === allValueDescriptions[0].datatype) -CipherBase.prototype._transform = function (data, _, next) { - var err - try { - if (this.hashMode) { - this._update(data) + if (allPrimitive && allDatatypesSame && values.length > 1) { + const nextElemId = this.nextOpId() + const datatype = allValueDescriptions[0].datatype + const values = allValueDescriptions.map(v => v.value) + const op = {action: 'set', obj: subpatch.objectId, elemId, insert: true, values, pred: []} + const edit = {action: 'multi-insert', elemId: nextElemId, index, values} + if (datatype) { + op.datatype = datatype + edit.datatype = datatype + } + this.addOp(op) + subpatch.edits.push(edit) } else { - this.push(this._update(data)) + for (let offset = 0; offset < values.length; offset++) { + let nextElemId = this.nextOpId() + const valuePatch = this.setValue(subpatch.objectId, index + offset, values[offset], true, [], elemId) + elemId = nextElemId + subpatch.edits.push({action: 'insert', index: index + offset, elemId, opId: elemId, value: valuePatch}) + } } - } catch (e) { - err = e - } finally { - next(err) } -} -CipherBase.prototype._flush = function (done) { - var err - try { - this.push(this.__final()) - } catch (e) { - err = e - } - - done(err) -} -CipherBase.prototype._finalOrDigest = function (outputEnc) { - var outData = this.__final() || Buffer.alloc(0) - if (outputEnc) { - outData = this._toString(outData, outputEnc, true) - } - return outData -} -CipherBase.prototype._toString = function (value, enc, fin) { - if (!this._decoder) { - this._decoder = new StringDecoder(enc) - this._encoding = enc - } + /** + * Updates the list object at path `path`, replacing the current value at + * position `index` with the new value `value`. + */ + setListIndex(path, index, value) { + const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId + const list = this.getObject(objectId) - if (this._encoding !== enc) throw new Error('can\'t switch encodings') + // Assignment past the end of the list => insert nulls followed by new value + if (index >= list.length) { + const insertions = createArrayOfNulls(index - list.length) + insertions.push(value) + return this.splice(path, list.length, 0, insertions) + } + if (list[index] instanceof Counter) { + throw new RangeError('Cannot overwrite a Counter object; use .increment() or .decrement() to change its value.') + } - var out = this._decoder.write(value) - if (fin) { - out += this._decoder.end() + // If the assigned list element value is the same as the existing value, and + // the assignment does not resolve a conflict, do nothing + if (list[index] !== value || Object.keys(list[CONFLICTS][index] || {}).length > 1 || value === undefined) { + this.applyAtPath(path, subpatch => { + const pred = getPred(list, index) + const opId = this.nextOpId() + const valuePatch = this.setValue(objectId, index, value, false, pred, getElemId(list, index)) + subpatch.edits.push({action: 'update', index, opId, value: valuePatch}) + }) + } } - return out -} - -module.exports = CipherBase + /** + * Updates the list object at path `path`, deleting `deletions` list elements starting from + * list index `start`, and inserting the list of new elements `insertions` at that position. + */ + splice(path, start, deletions, insertions) { + const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId + let list = this.getObject(objectId) + if (start < 0 || deletions < 0 || start > list.length - deletions) { + throw new RangeError(`${deletions} deletions starting at index ${start} are out of bounds for list of length ${list.length}`) + } + if (deletions === 0 && insertions.length === 0) return -},{"inherits":243,"safe-buffer":390,"stream":136,"string_decoder":400}],160:[function(require,module,exports){ -(function (Buffer){(function (){ -var elliptic = require('elliptic') -var BN = require('bn.js') + let patch = {diffs: {objectId: '_root', type: 'map', props: {}}} + let subpatch = this.getSubpatch(patch.diffs, path) -module.exports = function createECDH (curve) { - return new ECDH(curve) -} + if (deletions > 0) { + let op, lastElemParsed, lastPredParsed + for (let i = 0; i < deletions; i++) { + if (this.getObjectField(path, objectId, start + i) instanceof Counter) { + // This may seem bizarre, but it's really fiddly to implement deletion of counters from + // lists, and I doubt anyone ever needs to do this, so I'm just going to throw an + // exception for now. The reason is: a counter is created by a set operation with counter + // datatype, and subsequent increment ops are successors to the set operation. Normally, a + // set operation with successor indicates a value that has been overwritten, so a set + // operation with successors is normally invisible. Counters are an exception, because the + // increment operations don't make the set operation invisible. When a counter appears in + // a map, this is not too bad: if all successors are increments, then the counter remains + // visible; if one or more successors are deletions, it goes away. However, when deleting + // a list element, we have the additional challenge that we need to distinguish between a + // list element that is being deleted by the current change (in which case we need to put + // a 'remove' action in the patch's edits for that list) and a list element that was + // already deleted previously (in which case the patch should not reflect the deletion). + // This can be done, but as I said, it's fiddly. If someone wants to pick this up in the + // future, hopefully the above description will be enough to get you started. Good luck! + throw new TypeError('Unsupported operation: deleting a counter from a list') + } -var aliases = { - secp256k1: { - name: 'secp256k1', - byteLength: 32 - }, - secp224r1: { - name: 'p224', - byteLength: 28 - }, - prime256v1: { - name: 'p256', - byteLength: 32 - }, - prime192v1: { - name: 'p192', - byteLength: 24 - }, - ed25519: { - name: 'ed25519', - byteLength: 32 - }, - secp384r1: { - name: 'p384', - byteLength: 48 - }, - secp521r1: { - name: 'p521', - byteLength: 66 - } -} + // Any sequences of deletions with consecutive elemId and pred values get combined into a + // single multiOp; any others become individual deletion operations. This optimisation only + // kicks in if the user deletes a sequence of elements at once (in a single call to splice); + // it might be nice to also detect such runs of deletions in the case where the user deletes + // a sequence of list elements one by one. + const thisElem = getElemId(list, start + i), thisElemParsed = parseOpId(thisElem) + const thisPred = getPred(list, start + i) + const thisPredParsed = (thisPred.length === 1) ? parseOpId(thisPred[0]) : undefined -aliases.p224 = aliases.secp224r1 -aliases.p256 = aliases.secp256r1 = aliases.prime256v1 -aliases.p192 = aliases.secp192r1 = aliases.prime192v1 -aliases.p384 = aliases.secp384r1 -aliases.p521 = aliases.secp521r1 + if (op && lastElemParsed && lastPredParsed && thisPredParsed && + lastElemParsed.actorId === thisElemParsed.actorId && lastElemParsed.counter + 1 === thisElemParsed.counter && + lastPredParsed.actorId === thisPredParsed.actorId && lastPredParsed.counter + 1 === thisPredParsed.counter) { + op.multiOp = (op.multiOp || 1) + 1 + } else { + if (op) this.addOp(op) + op = {action: 'del', obj: objectId, elemId: thisElem, insert: false, pred: thisPred} + } + lastElemParsed = thisElemParsed + lastPredParsed = thisPredParsed + } + this.addOp(op) + subpatch.edits.push({action: 'remove', index: start, count: deletions}) + } -function ECDH (curve) { - this.curveType = aliases[curve] - if (!this.curveType) { - this.curveType = { - name: curve + if (insertions.length > 0) { + this.insertListItems(subpatch, start, insertions, false) } + this.applyPatch(patch.diffs, this.cache._root, this.updated) } - this.curve = new elliptic.ec(this.curveType.name) // eslint-disable-line new-cap - this.keys = void 0 -} -ECDH.prototype.generateKeys = function (enc, format) { - this.keys = this.curve.genKeyPair() - return this.getPublicKey(enc, format) -} + /** + * Updates the table object at path `path`, adding a new entry `row`. + * Returns the objectId of the new row. + */ + addTableRow(path, row) { + if (!isObject(row) || Array.isArray(row)) { + throw new TypeError('A table row must be an object') + } + if (row[OBJECT_ID]) { + throw new TypeError('Cannot reuse an existing object as table row') + } + if (row.id) { + throw new TypeError('A table row must not have an "id" property; it is generated automatically') + } -ECDH.prototype.computeSecret = function (other, inenc, enc) { - inenc = inenc || 'utf8' - if (!Buffer.isBuffer(other)) { - other = new Buffer(other, inenc) + const id = uuid() + const valuePatch = this.setValue(path[path.length - 1].objectId, id, row, false, []) + this.applyAtPath(path, subpatch => { + subpatch.props[id] = {[valuePatch.objectId]: valuePatch} + }) + return id } - var otherPub = this.curve.keyFromPublic(other).getPublic() - var out = otherPub.mul(this.keys.getPrivate()).getX() - return formatReturnValue(out, enc, this.curveType.byteLength) -} -ECDH.prototype.getPublicKey = function (enc, format) { - var key = this.keys.getPublic(format === 'compressed', true) - if (format === 'hybrid') { - if (key[key.length - 1] % 2) { - key[0] = 7 - } else { - key[0] = 6 + /** + * Updates the table object at path `path`, deleting the row with ID `rowId`. + * `pred` is the opId of the operation that originally created the row. + */ + deleteTableRow(path, rowId, pred) { + const objectId = path[path.length - 1].objectId, table = this.getObject(objectId) + + if (table.byId(rowId)) { + this.addOp({action: 'del', obj: objectId, key: rowId, insert: false, pred: [pred]}) + this.applyAtPath(path, subpatch => { + subpatch.props[rowId] = {} + }) } } - return formatReturnValue(key, enc) -} -ECDH.prototype.getPrivateKey = function (enc) { - return formatReturnValue(this.keys.getPrivate(), enc) -} + /** + * Adds the integer `delta` to the value of the counter located at property + * `key` in the object at path `path`. + */ + increment(path, key, delta) { + const objectId = path.length === 0 ? '_root' : path[path.length - 1].objectId + const object = this.getObject(objectId) + if (!(object[key] instanceof Counter)) { + throw new TypeError('Only counter values can be incremented') + } -ECDH.prototype.setPublicKey = function (pub, enc) { - enc = enc || 'utf8' - if (!Buffer.isBuffer(pub)) { - pub = new Buffer(pub, enc) - } - this.keys._importPublic(pub) - return this -} + // TODO what if there is a conflicting value on the same key as the counter? + const type = this.getObjectType(objectId) + const value = object[key].value + delta + const opId = this.nextOpId() + const pred = getPred(object, key) -ECDH.prototype.setPrivateKey = function (priv, enc) { - enc = enc || 'utf8' - if (!Buffer.isBuffer(priv)) { - priv = new Buffer(priv, enc) - } + if (type === 'list' || type === 'text') { + const elemId = getElemId(object, key, false) + this.addOp({action: 'inc', obj: objectId, elemId, value: delta, insert: false, pred}) + } else { + this.addOp({action: 'inc', obj: objectId, key, value: delta, insert: false, pred}) + } - var _priv = new BN(priv) - _priv = _priv.toString(16) - this.keys = this.curve.genKeyPair() - this.keys._importPrivate(_priv) - return this + this.applyAtPath(path, subpatch => { + if (type === 'list' || type === 'text') { + subpatch.edits.push({action: 'update', index: key, opId, value: {value, datatype: 'counter'}}) + } else { + subpatch.props[key] = {[opId]: {value, datatype: 'counter'}} + } + }) + } } -function formatReturnValue (bn, enc, len) { - if (!Array.isArray(bn)) { - bn = bn.toArray() - } - var buf = new Buffer(bn) - if (len && buf.length < len) { - var zeros = new Buffer(len - buf.length) - zeros.fill(0) - buf = Buffer.concat([zeros, buf]) - } - if (!enc) { - return buf +function getPred(object, key) { + if (object instanceof Table) { + return [object.opIds[key]] + } else if (object instanceof Text) { + return object.elems[key].pred + } else if (object[CONFLICTS]) { + return object[CONFLICTS][key] ? Object.keys(object[CONFLICTS][key]) : [] } else { - return buf.toString(enc) + return [] } } -}).call(this)}).call(this,require("buffer").Buffer) -},{"bn.js":161,"buffer":135,"elliptic":182}],161:[function(require,module,exports){ -arguments[4][45][0].apply(exports,arguments) -},{"buffer":91,"dup":45}],162:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var MD5 = require('md5.js') -var RIPEMD160 = require('ripemd160') -var sha = require('sha.js') -var Base = require('cipher-base') - -function Hash (hash) { - Base.call(this, 'digest') - - this._hash = hash -} - -inherits(Hash, Base) - -Hash.prototype._update = function (data) { - this._hash.update(data) -} - -Hash.prototype._final = function () { - return this._hash.digest() -} - -module.exports = function createHash (alg) { - alg = alg.toLowerCase() - if (alg === 'md5') return new MD5() - if (alg === 'rmd160' || alg === 'ripemd160') return new RIPEMD160() - - return new Hash(sha(alg)) +function getElemId(list, index, insert = false) { + if (insert) { + if (index === 0) return '_head' + index -= 1 + } + if (list[ELEM_IDS]) return list[ELEM_IDS][index] + if (list.getElemId) return list.getElemId(index) + throw new RangeError(`Cannot find elemId at list index ${index}`) } -},{"cipher-base":159,"inherits":243,"md5.js":285,"ripemd160":388,"sha.js":393}],163:[function(require,module,exports){ -var MD5 = require('md5.js') - -module.exports = function (buffer) { - return new MD5().update(buffer).digest() +module.exports = { + Context } -},{"md5.js":285}],164:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var Legacy = require('./legacy') -var Base = require('cipher-base') -var Buffer = require('safe-buffer').Buffer -var md5 = require('create-hash/md5') -var RIPEMD160 = require('ripemd160') - -var sha = require('sha.js') - -var ZEROS = Buffer.alloc(128) - -function Hmac (alg, key) { - Base.call(this, 'digest') - if (typeof key === 'string') { - key = Buffer.from(key) +},{"../src/common":101,"../src/uuid":102,"./apply_patch":90,"./constants":91,"./counter":93,"./numbers":95,"./table":98,"./text":99}],93:[function(require,module,exports){ +/** + * The most basic CRDT: an integer value that can be changed only by + * incrementing and decrementing. Since addition of integers is commutative, + * the value trivially converges. + */ +class Counter { + constructor(value) { + this.value = value || 0 + Object.freeze(this) } - var blocksize = (alg === 'sha512' || alg === 'sha384') ? 128 : 64 - - this._alg = alg - this._key = key - if (key.length > blocksize) { - var hash = alg === 'rmd160' ? new RIPEMD160() : sha(alg) - key = hash.update(key).digest() - } else if (key.length < blocksize) { - key = Buffer.concat([key, ZEROS], blocksize) + /** + * A peculiar JavaScript language feature from its early days: if the object + * `x` has a `valueOf()` method that returns a number, you can use numerical + * operators on the object `x` directly, such as `x + 1` or `x < 4`. + * This method is also called when coercing a value to a string by + * concatenating it with another string, as in `x + ''`. + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf + */ + valueOf() { + return this.value } - var ipad = this._ipad = Buffer.allocUnsafe(blocksize) - var opad = this._opad = Buffer.allocUnsafe(blocksize) + /** + * Returns the counter value as a decimal string. If `x` is a counter object, + * this method is called e.g. when you do `['value: ', x].join('')` or when + * you use string interpolation: `value: ${x}`. + */ + toString() { + return this.valueOf().toString() + } - for (var i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36 - opad[i] = key[i] ^ 0x5C + /** + * Returns the counter value, so that a JSON serialization of an Automerge + * document represents the counter simply as an integer. + */ + toJSON() { + return this.value } - this._hash = alg === 'rmd160' ? new RIPEMD160() : sha(alg) - this._hash.update(ipad) } -inherits(Hmac, Base) +/** + * An instance of this class is used when a counter is accessed within a change + * callback. + */ +class WriteableCounter extends Counter { + /** + * Increases the value of the counter by `delta`. If `delta` is not given, + * increases the value of the counter by 1. + */ + increment(delta) { + delta = typeof delta === 'number' ? delta : 1 + this.context.increment(this.path, this.key, delta) + this.value += delta + return this.value + } -Hmac.prototype._update = function (data) { - this._hash.update(data) + /** + * Decreases the value of the counter by `delta`. If `delta` is not given, + * decreases the value of the counter by 1. + */ + decrement(delta) { + return this.increment(typeof delta === 'number' ? -delta : -1) + } } -Hmac.prototype._final = function () { - var h = this._hash.digest() - var hash = this._alg === 'rmd160' ? new RIPEMD160() : sha(this._alg) - return hash.update(this._opad).update(h).digest() +/** + * Returns an instance of `WriteableCounter` for use in a change callback. + * `context` is the proxy context that keeps track of the mutations. + * `objectId` is the ID of the object containing the counter, and `key` is + * the property name (key in map, or index in list) where the counter is + * located. +*/ +function getWriteableCounter(value, context, path, objectId, key) { + const instance = Object.create(WriteableCounter.prototype) + instance.value = value + instance.context = context + instance.path = path + instance.objectId = objectId + instance.key = key + return instance } -module.exports = function createHmac (alg, key) { - alg = alg.toLowerCase() - if (alg === 'rmd160' || alg === 'ripemd160') { - return new Hmac('rmd160', key) +module.exports = { Counter, getWriteableCounter } + +},{}],94:[function(require,module,exports){ +const { OPTIONS, CACHE, STATE, OBJECT_ID, CONFLICTS, CHANGE, ELEM_IDS } = require('./constants') +const { isObject, copyObject } = require('../src/common') +const uuid = require('../src/uuid') +const { interpretPatch, cloneRootObject } = require('./apply_patch') +const { rootObjectProxy } = require('./proxies') +const { Context } = require('./context') +const { Text } = require('./text') +const { Table } = require('./table') +const { Counter } = require('./counter') +const { Float64, Int, Uint } = require('./numbers') +const { Observable } = require('./observable') + +/** + * Actor IDs must consist only of hexadecimal digits so that they can be encoded + * compactly in binary form. + */ +function checkActorId(actorId) { + if (typeof actorId !== 'string') { + throw new TypeError(`Unsupported type of actorId: ${typeof actorId}`) } - if (alg === 'md5') { - return new Legacy(md5, key) + if (!/^[0-9a-f]+$/.test(actorId)) { + throw new RangeError('actorId must consist only of lowercase hex digits') + } + if (actorId.length % 2 !== 0) { + throw new RangeError('actorId must consist of an even number of digits') } - return new Hmac(alg, key) } -},{"./legacy":165,"cipher-base":159,"create-hash/md5":163,"inherits":243,"ripemd160":388,"safe-buffer":390,"sha.js":393}],165:[function(require,module,exports){ -'use strict' -var inherits = require('inherits') -var Buffer = require('safe-buffer').Buffer - -var Base = require('cipher-base') - -var ZEROS = Buffer.alloc(128) -var blocksize = 64 - -function Hmac (alg, key) { - Base.call(this, 'digest') - if (typeof key === 'string') { - key = Buffer.from(key) +/** + * Takes a set of objects that have been updated (in `updated`) and an updated state object + * `state`, and returns a new immutable document root object based on `doc` that reflects + * those updates. + */ +function updateRootObject(doc, updated, state) { + let newDoc = updated._root + if (!newDoc) { + newDoc = cloneRootObject(doc[CACHE]._root) + updated._root = newDoc } + Object.defineProperty(newDoc, OPTIONS, {value: doc[OPTIONS]}) + Object.defineProperty(newDoc, CACHE, {value: updated}) + Object.defineProperty(newDoc, STATE, {value: state}) - this._alg = alg - this._key = key - - if (key.length > blocksize) { - key = alg(key) - } else if (key.length < blocksize) { - key = Buffer.concat([key, ZEROS], blocksize) + if (doc[OPTIONS].freeze) { + for (let objectId of Object.keys(updated)) { + if (updated[objectId] instanceof Table) { + updated[objectId]._freeze() + } else if (updated[objectId] instanceof Text) { + Object.freeze(updated[objectId].elems) + Object.freeze(updated[objectId]) + } else { + Object.freeze(updated[objectId]) + Object.freeze(updated[objectId][CONFLICTS]) + } + } } - var ipad = this._ipad = Buffer.allocUnsafe(blocksize) - var opad = this._opad = Buffer.allocUnsafe(blocksize) - - for (var i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36 - opad[i] = key[i] ^ 0x5C + for (let objectId of Object.keys(doc[CACHE])) { + if (!updated[objectId]) { + updated[objectId] = doc[CACHE][objectId] + } } - this._hash = [ipad] -} - -inherits(Hmac, Base) - -Hmac.prototype._update = function (data) { - this._hash.push(data) + if (doc[OPTIONS].freeze) { + Object.freeze(updated) + } + return newDoc } -Hmac.prototype._final = function () { - var h = this._alg(Buffer.concat(this._hash)) - return this._alg(Buffer.concat([this._opad, h])) -} -module.exports = Hmac +/** + * Adds a new change request to the list of pending requests, and returns an + * updated document root object. + * The details of the change are taken from the context object `context`. + * `options` contains properties that may affect how the change is processed; in + * particular, the `message` property of `options` is an optional human-readable + * string describing the change. + */ +function makeChange(doc, context, options) { + const actor = getActorId(doc) + if (!actor) { + throw new Error('Actor ID must be initialized with setActorId() before making a change') + } + const state = copyObject(doc[STATE]) + state.seq += 1 -},{"cipher-base":159,"inherits":243,"safe-buffer":390}],166:[function(require,module,exports){ -'use strict' + const change = { + actor, + seq: state.seq, + startOp: state.maxOp + 1, + deps: state.deps, + time: (options && typeof options.time === 'number') ? options.time + : Math.round(new Date().getTime() / 1000), + message: (options && typeof options.message === 'string') ? options.message : '', + ops: context.ops + } -exports.randomBytes = exports.rng = exports.pseudoRandomBytes = exports.prng = require('randombytes') -exports.createHash = exports.Hash = require('create-hash') -exports.createHmac = exports.Hmac = require('create-hmac') + if (doc[OPTIONS].backend) { + const [backendState, patch, binaryChange] = doc[OPTIONS].backend.applyLocalChange(state.backendState, change) + state.backendState = backendState + state.lastLocalChange = binaryChange + // NOTE: When performing a local change, the patch is effectively applied twice -- once by the + // context invoking interpretPatch as soon as any change is made, and the second time here + // (after a round-trip through the backend). This is perhaps more robust, as changes only take + // effect in the form processed by the backend, but the downside is a performance cost. + // Should we change this? + const newDoc = applyPatchToDoc(doc, patch, state, true) + const patchCallback = options && options.patchCallback || doc[OPTIONS].patchCallback + if (patchCallback) patchCallback(patch, doc, newDoc, true, [binaryChange]) + return [newDoc, change] -var algos = require('browserify-sign/algos') -var algoKeys = Object.keys(algos) -var hashes = ['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'md5', 'rmd160'].concat(algoKeys) -exports.getHashes = function () { - return hashes + } else { + const queuedRequest = {actor, seq: change.seq, before: doc} + state.requests = state.requests.concat([queuedRequest]) + state.maxOp = state.maxOp + countOps(change.ops) + state.deps = [] + return [updateRootObject(doc, context ? context.updated : {}, state), change] + } } -var p = require('pbkdf2') -exports.pbkdf2 = p.pbkdf2 -exports.pbkdf2Sync = p.pbkdf2Sync - -var aes = require('browserify-cipher') - -exports.Cipher = aes.Cipher -exports.createCipher = aes.createCipher -exports.Cipheriv = aes.Cipheriv -exports.createCipheriv = aes.createCipheriv -exports.Decipher = aes.Decipher -exports.createDecipher = aes.createDecipher -exports.Decipheriv = aes.Decipheriv -exports.createDecipheriv = aes.createDecipheriv -exports.getCiphers = aes.getCiphers -exports.listCiphers = aes.listCiphers - -var dh = require('diffie-hellman') - -exports.DiffieHellmanGroup = dh.DiffieHellmanGroup -exports.createDiffieHellmanGroup = dh.createDiffieHellmanGroup -exports.getDiffieHellman = dh.getDiffieHellman -exports.createDiffieHellman = dh.createDiffieHellman -exports.DiffieHellman = dh.DiffieHellman - -var sign = require('browserify-sign') - -exports.createSign = sign.createSign -exports.Sign = sign.Sign -exports.createVerify = sign.createVerify -exports.Verify = sign.Verify - -exports.createECDH = require('create-ecdh') - -var publicEncrypt = require('public-encrypt') - -exports.publicEncrypt = publicEncrypt.publicEncrypt -exports.privateEncrypt = publicEncrypt.privateEncrypt -exports.publicDecrypt = publicEncrypt.publicDecrypt -exports.privateDecrypt = publicEncrypt.privateDecrypt - -// the least I can do is make error messages for the rest of the node.js/crypto api. -// ;[ -// 'createCredentials' -// ].forEach(function (name) { -// exports[name] = function () { -// throw new Error([ -// 'sorry, ' + name + ' is not implemented yet', -// 'we accept pull requests', -// 'https://github.com/crypto-browserify/crypto-browserify' -// ].join('\n')) -// } -// }) - -var rf = require('randomfill') - -exports.randomFill = rf.randomFill -exports.randomFillSync = rf.randomFillSync - -exports.createCredentials = function () { - throw new Error([ - 'sorry, createCredentials is not implemented yet', - 'we accept pull requests', - 'https://github.com/crypto-browserify/crypto-browserify' - ].join('\n')) +function countOps(ops) { + let count = 0 + for (const op of ops) { + if (op.action === 'set' && op.values) { + count += op.values.length + } else { + count += 1 + } + } + return count } -exports.constants = { - 'DH_CHECK_P_NOT_SAFE_PRIME': 2, - 'DH_CHECK_P_NOT_PRIME': 1, - 'DH_UNABLE_TO_CHECK_GENERATOR': 4, - 'DH_NOT_SUITABLE_GENERATOR': 8, - 'NPN_ENABLED': 1, - 'ALPN_ENABLED': 1, - 'RSA_PKCS1_PADDING': 1, - 'RSA_SSLV23_PADDING': 2, - 'RSA_NO_PADDING': 3, - 'RSA_PKCS1_OAEP_PADDING': 4, - 'RSA_X931_PADDING': 5, - 'RSA_PKCS1_PSS_PADDING': 6, - 'POINT_CONVERSION_COMPRESSED': 2, - 'POINT_CONVERSION_UNCOMPRESSED': 4, - 'POINT_CONVERSION_HYBRID': 6 +/** + * Returns the binary encoding of the last change made by the local actor. + */ +function getLastLocalChange(doc) { + return doc[STATE] && doc[STATE].lastLocalChange ? doc[STATE].lastLocalChange : null } -},{"browserify-cipher":109,"browserify-sign":116,"browserify-sign/algos":113,"create-ecdh":160,"create-hash":162,"create-hmac":164,"diffie-hellman":177,"pbkdf2":371,"public-encrypt":378,"randombytes":386,"randomfill":387}],167:[function(require,module,exports){ -'use strict' - -const { AbstractChainedBatch } = require('abstract-leveldown') -const kOperations = Symbol('operations') +/** + * Applies the changes described in `patch` to the document with root object + * `doc`. The state object `state` is attached to the new root object. + * `fromBackend` should be set to `true` if the patch came from the backend, + * and to `false` if the patch is a transient local (optimistically applied) + * change from the frontend. + */ +function applyPatchToDoc(doc, patch, state, fromBackend) { + const actor = getActorId(doc) + const updated = {} + interpretPatch(patch.diffs, doc, updated) -module.exports = class DeferredChainedBatch extends AbstractChainedBatch { - constructor (db) { - super(db) - this[kOperations] = [] + if (fromBackend) { + if (!patch.clock) throw new RangeError('patch is missing clock field') + if (patch.clock[actor] && patch.clock[actor] > state.seq) { + state.seq = patch.clock[actor] + } + state.clock = patch.clock + state.deps = patch.deps + state.maxOp = Math.max(state.maxOp, patch.maxOp) } + return updateRootObject(doc, updated, state) +} - _put (key, value, options) { - this[kOperations].push({ ...options, type: 'put', key, value }) +/** + * Creates an empty document object with no changes. + */ +function init(options) { + if (typeof options === 'string') { + options = {actorId: options} + } else if (typeof options === 'undefined') { + options = {} + } else if (!isObject(options)) { + throw new TypeError(`Unsupported value for init() options: ${options}`) } - _del (key, options) { - this[kOperations].push({ ...options, type: 'del', key }) + if (!options.deferActorId) { + if (options.actorId === undefined) { + options.actorId = uuid() + } + checkActorId(options.actorId) } - _clear () { - this[kOperations] = [] + if (options.observable) { + const patchCallback = options.patchCallback, observable = options.observable + options.patchCallback = (patch, before, after, local, changes) => { + if (patchCallback) patchCallback(patch, before, after, local, changes) + observable.patchCallback(patch, before, after, local, changes) + } } - _write (options, callback) { - // AbstractChainedBatch would call _batch(), we call batch() - this.db.batch(this[kOperations], options, callback) + const root = {}, cache = {_root: root} + const state = {seq: 0, maxOp: 0, requests: [], clock: {}, deps: []} + if (options.backend) { + state.backendState = options.backend.init() + state.lastLocalChange = null } + Object.defineProperty(root, OBJECT_ID, {value: '_root'}) + Object.defineProperty(root, OPTIONS, {value: Object.freeze(options)}) + Object.defineProperty(root, CONFLICTS, {value: Object.freeze({})}) + Object.defineProperty(root, CACHE, {value: Object.freeze(cache)}) + Object.defineProperty(root, STATE, {value: Object.freeze(state)}) + return Object.freeze(root) } -},{"abstract-leveldown":28}],168:[function(require,module,exports){ -'use strict' +/** + * Returns a new document object initialized with the given state. + */ +function from(initialState, options) { + return change(init(options), 'Initialization', doc => Object.assign(doc, initialState)) +} -const { AbstractIterator } = require('abstract-leveldown') -const inherits = require('inherits') -const getCallback = require('./util').getCallback -const kOptions = Symbol('options') -const kIterator = Symbol('iterator') -const kOperations = Symbol('operations') -const kPromise = Symbol('promise') +/** + * Changes a document `doc` according to actions taken by the local user. + * `options` is an object that can contain the following properties: + * - `message`: an optional descriptive string that is attached to the change. + * If `options` is a string, it is treated as `message`. + * + * The actual change is made within the callback function `callback`, which is + * given a mutable version of the document as argument. Returns a two-element + * array `[doc, request]` where `doc` is the updated document, and `request` + * is the change request to send to the backend. If nothing was actually + * changed, returns the original `doc` and a `null` change request. + */ +function change(doc, options, callback) { + if (doc[OBJECT_ID] !== '_root') { + throw new TypeError('The first argument to Automerge.change must be the document root') + } + if (doc[CHANGE]) { + throw new TypeError('Calls to Automerge.change cannot be nested') + } + if (typeof options === 'function' && callback === undefined) { + [options, callback] = [callback, options] + } + if (typeof options === 'string') { + options = {message: options} + } + if (options !== undefined && !isObject(options)) { + throw new TypeError('Unsupported type of options') + } -function DeferredIterator (db, options) { - AbstractIterator.call(this, db) + const actorId = getActorId(doc) + if (!actorId) { + throw new Error('Actor ID must be initialized with setActorId() before making a change') + } + const context = new Context(doc, actorId) + callback(rootObjectProxy(context)) - this[kOptions] = options - this[kIterator] = null - this[kOperations] = [] + if (Object.keys(context.updated).length === 0) { + // If the callback didn't change anything, return the original document object unchanged + return [doc, null] + } else { + return makeChange(doc, context, options) + } } -inherits(DeferredIterator, AbstractIterator) - -DeferredIterator.prototype.setDb = function (db) { - this[kIterator] = db.iterator(this[kOptions]) +/** + * Triggers a new change request on the document `doc` without actually + * modifying its data. `options` is an object as described in the documentation + * for the `change` function. This function can be useful for acknowledging the + * receipt of some message (as it's incorported into the `deps` field of the + * change). Returns a two-element array `[doc, request]` where `doc` is the + * updated document, and `request` is the change request to send to the backend. + */ +function emptyChange(doc, options) { + if (doc[OBJECT_ID] !== '_root') { + throw new TypeError('The first argument to Automerge.emptyChange must be the document root') + } + if (typeof options === 'string') { + options = {message: options} + } + if (options !== undefined && !isObject(options)) { + throw new TypeError('Unsupported type of options') + } - for (const op of this[kOperations].splice(0, this[kOperations].length)) { - this[kIterator][op.method](...op.args) + const actorId = getActorId(doc) + if (!actorId) { + throw new Error('Actor ID must be initialized with setActorId() before making a change') } + return makeChange(doc, new Context(doc, actorId), options) } -DeferredIterator.prototype.next = function (...args) { - if (this.db.status === 'open') { - return this[kIterator].next(...args) +/** + * Applies `patch` to the document root object `doc`. This patch must come + * from the backend; it may be the result of a local change or a remote change. + * If it is the result of a local change, the `seq` field from the change + * request should be included in the patch, so that we can match them up here. + */ +function applyPatch(doc, patch, backendState = undefined) { + if (doc[OBJECT_ID] !== '_root') { + throw new TypeError('The first argument to Frontend.applyPatch must be the document root') } + const state = copyObject(doc[STATE]) - const callback = getCallback(args, kPromise, function map (key, value) { - if (key === undefined && value === undefined) { - return undefined - } else { - return [key, value] + if (doc[OPTIONS].backend) { + if (!backendState) { + throw new RangeError('applyPatch must be called with the updated backend state') } - }) - - if (this.db.status === 'opening') { - this[kOperations].push({ method: 'next', args }) - } else { - this._nextTick(callback, new Error('Database is not open')) + state.backendState = backendState + return applyPatchToDoc(doc, patch, state, true) } - return callback[kPromise] || this -} + let baseDoc -DeferredIterator.prototype.seek = function (...args) { - if (this.db.status === 'open') { - this[kIterator].seek(...args) - } else if (this.db.status === 'opening') { - this[kOperations].push({ method: 'seek', args }) + if (state.requests.length > 0) { + baseDoc = state.requests[0].before + if (patch.actor === getActorId(doc)) { + if (state.requests[0].seq !== patch.seq) { + throw new RangeError(`Mismatched sequence number: patch ${patch.seq} does not match next request ${state.requests[0].seq}`) + } + state.requests = state.requests.slice(1) + } else { + state.requests = state.requests.slice() + } } else { - throw new Error('Database is not open') - } -} - -DeferredIterator.prototype.end = function (...args) { - if (this.db.status === 'open') { - return this[kIterator].end(...args) + baseDoc = doc + state.requests = [] } - const callback = getCallback(args, kPromise) - - if (this.db.status === 'opening') { - this[kOperations].push({ method: 'end', args }) + let newDoc = applyPatchToDoc(baseDoc, patch, state, true) + if (state.requests.length === 0) { + return newDoc } else { - this._nextTick(callback, new Error('Database is not open')) + state.requests[0] = copyObject(state.requests[0]) + state.requests[0].before = newDoc + return updateRootObject(doc, {}, state) } +} - return callback[kPromise] || this +/** + * Returns the Automerge object ID of the given object. + */ +function getObjectId(object) { + return object[OBJECT_ID] } -for (const method of ['next', 'seek', 'end']) { - DeferredIterator.prototype['_' + method] = function () { - /* istanbul ignore next: assertion */ - throw new Error('Did not expect private method to be called: ' + method) +/** + * Returns the object with the given Automerge object ID. Note: when called + * within a change callback, the returned object is read-only (not a mutable + * proxy object). + */ +function getObjectById(doc, objectId) { + // It would be nice to return a proxied object in a change callback. + // However, that requires knowing the path from the root to the current + // object, which we don't have if we jumped straight to the object by its ID. + // If we maintained an index from object ID to parent ID we could work out the path. + if (doc[CHANGE]) { + throw new TypeError('Cannot use getObjectById in a change callback') } + return doc[CACHE][objectId] } -module.exports = DeferredIterator - -},{"./util":170,"abstract-leveldown":28,"inherits":243}],169:[function(require,module,exports){ -'use strict' - -const { AbstractLevelDOWN } = require('abstract-leveldown') -const inherits = require('inherits') -const DeferredIterator = require('./deferred-iterator') -const DeferredChainedBatch = require('./deferred-chained-batch') -const getCallback = require('./util').getCallback - -const deferrables = ['put', 'get', 'getMany', 'del', 'batch', 'clear'] -const optionalDeferrables = ['approximateSize', 'compactRange'] +/** + * Returns the Automerge actor ID of the given document. + */ +function getActorId(doc) { + return doc[STATE].actorId || doc[OPTIONS].actorId +} -const kInnerDb = Symbol('innerDb') -const kOperations = Symbol('operations') -const kPromise = Symbol('promise') +/** + * Sets the Automerge actor ID on the document object `doc`, returning a + * document object with updated metadata. + */ +function setActorId(doc, actorId) { + checkActorId(actorId) + const state = Object.assign({}, doc[STATE], {actorId}) + return updateRootObject(doc, {}, state) +} -function DeferredLevelDOWN (db) { - AbstractLevelDOWN.call(this, db.supports || {}) +/** + * Fetches the conflicts on the property `key` of `object`, which may be any + * object in a document. If `object` is a list, then `key` must be a list + * index; if `object` is a map, then `key` must be a property name. + */ +function getConflicts(object, key) { + if (object[CONFLICTS] && object[CONFLICTS][key] && + Object.keys(object[CONFLICTS][key]).length > 1) { + return object[CONFLICTS][key] + } +} - // TODO (future major): remove this fallback; db must have manifest that - // declares approximateSize and compactRange in additionalMethods. - for (const m of optionalDeferrables) { - if (typeof db[m] === 'function' && !this.supports.additionalMethods[m]) { - this.supports.additionalMethods[m] = true +/** + * Returns the backend state associated with the document `doc` (only used if + * a backend implementation is passed to `init()`). + */ +function getBackendState(doc, callerName = null, argPos = 'first') { + if (doc[OBJECT_ID] !== '_root') { + // Most likely cause of passing an array here is forgetting to deconstruct the return value of + // Automerge.applyChanges(). + const extraMsg = Array.isArray(doc) ? '. Note: Automerge.applyChanges now returns an array.' : '' + if (callerName) { + throw new TypeError(`The ${argPos} argument to Automerge.${callerName} must be the document root${extraMsg}`) + } else { + throw new TypeError(`Argument is not an Automerge document root${extraMsg}`) } } + return doc[STATE].backendState +} - this[kInnerDb] = db - this[kOperations] = [] - - implement(this) +/** + * Given an array or text object from an Automerge document, returns an array + * containing the unique element ID of each list element/character. + */ +function getElementIds(list) { + if (list instanceof Text) { + return list.elems.map(elem => elem.elemId) + } else { + return list[ELEM_IDS] + } } -inherits(DeferredLevelDOWN, AbstractLevelDOWN) +module.exports = { + init, from, change, emptyChange, applyPatch, + getObjectId, getObjectById, getActorId, setActorId, getConflicts, getLastLocalChange, + getBackendState, getElementIds, + Text, Table, Counter, Observable, Float64, Int, Uint +} -DeferredLevelDOWN.prototype.type = 'deferred-leveldown' +},{"../src/common":101,"../src/uuid":102,"./apply_patch":90,"./constants":91,"./context":92,"./counter":93,"./numbers":95,"./observable":96,"./proxies":97,"./table":98,"./text":99}],95:[function(require,module,exports){ +// Convience classes to allow users to stricly specify the number type they want -// Backwards compatibility for reachdown and subleveldown -Object.defineProperty(DeferredLevelDOWN.prototype, '_db', { - enumerable: true, - get () { - return this[kInnerDb] +class Int { + constructor(value) { + if (!(Number.isInteger(value) && value <= Number.MAX_SAFE_INTEGER && value >= Number.MIN_SAFE_INTEGER)) { + throw new RangeError(`Value ${value} cannot be a uint`) + } + this.value = value + Object.freeze(this) } -}) +} -DeferredLevelDOWN.prototype._open = function (options, callback) { - const onopen = (err) => { - if (err || this[kInnerDb].status !== 'open') { - // TODO: reject scheduled operations - return callback(err || new Error('Database is not open')) +class Uint { + constructor(value) { + if (!(Number.isInteger(value) && value <= Number.MAX_SAFE_INTEGER && value >= 0)) { + throw new RangeError(`Value ${value} cannot be a uint`) } + this.value = value + Object.freeze(this) + } +} - const operations = this[kOperations] - this[kOperations] = [] - - for (const op of operations) { - if (op.iterator) { - op.iterator.setDb(this[kInnerDb]) - } else { - this[kInnerDb][op.method](...op.args) - } +class Float64 { + constructor(value) { + if (typeof value !== 'number') { + throw new RangeError(`Value ${value} cannot be a float64`) } + this.value = value || 0.0 + Object.freeze(this) + } +} - /* istanbul ignore if: assertion */ - if (this[kOperations].length > 0) { - throw new Error('Did not expect further operations') - } +module.exports = { Int, Uint, Float64 } - callback() - } +},{}],96:[function(require,module,exports){ +const { OBJECT_ID, CONFLICTS } = require('./constants') - if (this[kInnerDb].status === 'new' || this[kInnerDb].status === 'closed') { - this[kInnerDb].open(options, onopen) - } else { - this._nextTick(onopen) +/** + * Allows an application to register a callback when a particular object in + * a document changes. + * + * NOTE: This API is experimental and may change without warning in minor releases. + */ +class Observable { + constructor() { + this.observers = {} // map from objectId to array of observers for that object } -} -DeferredLevelDOWN.prototype._close = function (callback) { - this[kInnerDb].close(callback) -} + /** + * Called by an Automerge document when `patch` is applied. `before` is the + * state of the document before the patch, and `after` is the state after + * applying it. `local` is true if the update is a result of locally calling + * `Automerge.change()`, and false otherwise. `changes` is an array of + * changes that were applied to the document (as Uint8Arrays). + */ + patchCallback(patch, before, after, local, changes) { + this._objectUpdate(patch.diffs, before, after, local, changes) + } -DeferredLevelDOWN.prototype._isOperational = function () { - return this.status === 'opening' -} + /** + * Recursively walks a patch and calls the callbacks for all objects that + * appear in the patch. + */ + _objectUpdate(diff, before, after, local, changes) { + if (!diff.objectId) return + if (this.observers[diff.objectId]) { + for (let callback of this.observers[diff.objectId]) { + callback(diff, before, after, local, changes) + } + } -function implement (self) { - const additionalMethods = Object.keys(self.supports.additionalMethods) + if (diff.type === 'map' && diff.props) { + for (const propName of Object.keys(diff.props)) { + for (const opId of Object.keys(diff.props[propName])) { + this._objectUpdate(diff.props[propName][opId], + before && before[CONFLICTS] && before[CONFLICTS][propName] && before[CONFLICTS][propName][opId], + after && after[CONFLICTS] && after[CONFLICTS][propName] && after[CONFLICTS][propName][opId], + local, changes) + } + } - for (const method of deferrables.concat(additionalMethods)) { - // Override the public rather than private methods to cover cases where abstract-leveldown - // has a fast-path like on db.batch([]) which bypasses _batch() because the array is empty. - self[method] = function (...args) { - if (method === 'batch' && args.length === 0) { - return new DeferredChainedBatch(this) - } else if (this.status === 'open') { - return this[kInnerDb][method](...args) + } else if (diff.type === 'table' && diff.props) { + for (const rowId of Object.keys(diff.props)) { + for (const opId of Object.keys(diff.props[rowId])) { + this._objectUpdate(diff.props[rowId][opId], + before && before.byId(rowId), + after && after.byId(rowId), + local, changes) + } } - const callback = getCallback(args, kPromise) + } else if (diff.type === 'list' && diff.edits) { + let offset = 0 + for (const edit of diff.edits) { + if (edit.action === 'insert') { + offset -= 1 + this._objectUpdate(edit.value, undefined, + after && after[CONFLICTS] && after[CONFLICTS][edit.index] && after[CONFLICTS][edit.index][edit.elemId], + local, changes) + } else if (edit.action === 'multi-insert') { + offset -= edit.values.length + } else if (edit.action === 'update') { + this._objectUpdate(edit.value, + before && before[CONFLICTS] && before[CONFLICTS][edit.index + offset] && + before[CONFLICTS][edit.index + offset][edit.opId], + after && after[CONFLICTS] && after[CONFLICTS][edit.index] && after[CONFLICTS][edit.index][edit.opId], + local, changes) + } else if (edit.action === 'remove') { + offset += edit.count + } + } - if (this.status === 'opening') { - this[kOperations].push({ method, args }) - } else { - this._nextTick(callback, new Error('Database is not open')) + } else if (diff.type === 'text' && diff.edits) { + let offset = 0 + for (const edit of diff.edits) { + if (edit.action === 'insert') { + offset -= 1 + this._objectUpdate(edit.value, undefined, after && after.get(edit.index), local, changes) + } else if (edit.action === 'multi-insert') { + offset -= edit.values.length + } else if (edit.action === 'update') { + this._objectUpdate(edit.value, + before && before.get(edit.index + offset), + after && after.get(edit.index), + local, changes) + } else if (edit.action === 'remove') { + offset += edit.count + } } - - return callback[kPromise] - } - } - - self.iterator = function (options) { - if (this.status === 'open') { - return this[kInnerDb].iterator(options) - } else if (this.status === 'opening') { - const iterator = new DeferredIterator(this, options) - this[kOperations].push({ iterator }) - return iterator - } else { - throw new Error('Database is not open') } } - for (const method of deferrables.concat(['iterator'])) { - self['_' + method] = function () { - /* istanbul ignore next: assertion */ - throw new Error('Did not expect private method to be called: ' + method) - } + /** + * Call this to register a callback that will get called whenever a particular + * object in a document changes. The callback is passed five arguments: the + * part of the patch describing the update to that object, the old state of + * the object, the new state of the object, a boolean that is true if the + * change is the result of calling `Automerge.change()` locally, and the array + * of binary changes applied to the document. + */ + observe(object, callback) { + const objectId = object[OBJECT_ID] + if (!objectId) throw new TypeError('The observed object must be part of an Automerge document') + if (!this.observers[objectId]) this.observers[objectId] = [] + this.observers[objectId].push(callback) } } -module.exports = DeferredLevelDOWN -module.exports.DeferredIterator = DeferredIterator - -},{"./deferred-chained-batch":167,"./deferred-iterator":168,"./util":170,"abstract-leveldown":28,"inherits":243}],170:[function(require,module,exports){ -'use strict' - -exports.getCallback = function (args, symbol, map) { - let callback = args[args.length - 1] +module.exports = { Observable } - if (typeof callback !== 'function') { - const promise = new Promise((resolve, reject) => { - args.push(callback = function (err, ...results) { - if (err) reject(err) - else resolve(map ? map(...results) : results[0]) - }) - }) +},{"./constants":91}],97:[function(require,module,exports){ +const { OBJECT_ID, CHANGE, STATE } = require('./constants') +const { createArrayOfNulls } = require('../src/common') +const { Text } = require('./text') +const { Table } = require('./table') - callback[symbol] = promise +function parseListIndex(key) { + if (typeof key === 'string' && /^[0-9]+$/.test(key)) key = parseInt(key, 10) + if (typeof key !== 'number') { + throw new TypeError('A list index must be a number, but you passed ' + JSON.stringify(key)) } - - return callback + if (key < 0 || isNaN(key) || key === Infinity || key === -Infinity) { + throw new RangeError('A list index must be positive, but you passed ' + key) + } + return key } -},{}],171:[function(require,module,exports){ -'use strict'; +function listMethods(context, listId, path) { + const methods = { + deleteAt(index, numDelete) { + context.splice(path, parseListIndex(index), numDelete || 1, []) + return this + }, -exports.utils = require('./des/utils'); -exports.Cipher = require('./des/cipher'); -exports.DES = require('./des/des'); -exports.CBC = require('./des/cbc'); -exports.EDE = require('./des/ede'); + fill(value, start, end) { + let list = context.getObject(listId) + for (let index = parseListIndex(start || 0); index < parseListIndex(end || list.length); index++) { + context.setListIndex(path, index, value) + } + return this + }, -},{"./des/cbc":172,"./des/cipher":173,"./des/des":174,"./des/ede":175,"./des/utils":176}],172:[function(require,module,exports){ -'use strict'; + indexOf(o, start = 0) { + const id = o[OBJECT_ID] + if (id) { + const list = context.getObject(listId) + for (let index = start; index < list.length; index++) { + if (list[index][OBJECT_ID] === id) { + return index + } + } + return -1 + } else { + return context.getObject(listId).indexOf(o, start) + } + }, -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); + insertAt(index, ...values) { + context.splice(path, parseListIndex(index), 0, values) + return this + }, -var proto = {}; + pop() { + let list = context.getObject(listId) + if (list.length == 0) return + const last = context.getObjectField(path, listId, list.length - 1) + context.splice(path, list.length - 1, 1, []) + return last + }, -function CBCState(iv) { - assert.equal(iv.length, 8, 'Invalid IV length'); + push(...values) { + let list = context.getObject(listId) + context.splice(path, list.length, 0, values) + // need to getObject() again because the list object above may be immutable + return context.getObject(listId).length + }, - this.iv = new Array(8); - for (var i = 0; i < this.iv.length; i++) - this.iv[i] = iv[i]; -} + shift() { + let list = context.getObject(listId) + if (list.length == 0) return + const first = context.getObjectField(path, listId, 0) + context.splice(path, 0, 1, []) + return first + }, -function instantiate(Base) { - function CBC(options) { - Base.call(this, options); - this._cbcInit(); + splice(start, deleteCount, ...values) { + let list = context.getObject(listId) + start = parseListIndex(start) + if (deleteCount === undefined || deleteCount > list.length - start) { + deleteCount = list.length - start + } + const deleted = [] + for (let n = 0; n < deleteCount; n++) { + deleted.push(context.getObjectField(path, listId, start + n)) + } + context.splice(path, start, deleteCount, values) + return deleted + }, + + unshift(...values) { + context.splice(path, 0, 0, values) + return context.getObject(listId).length + } } - inherits(CBC, Base); - var keys = Object.keys(proto); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - CBC.prototype[key] = proto[key]; + for (let iterator of ['entries', 'keys', 'values']) { + let list = context.getObject(listId) + methods[iterator] = () => list[iterator]() } - CBC.create = function create(options) { - return new CBC(options); - }; + // Read-only methods that can delegate to the JavaScript built-in implementations + for (let method of ['concat', 'every', 'filter', 'find', 'findIndex', 'forEach', 'includes', + 'join', 'lastIndexOf', 'map', 'reduce', 'reduceRight', + 'slice', 'some', 'toLocaleString', 'toString']) { + methods[method] = (...args) => { + const list = context.getObject(listId) + .map((item, index) => context.getObjectField(path, listId, index)) + return list[method](...args) + } + } - return CBC; + return methods } -exports.instantiate = instantiate; - -proto._cbcInit = function _cbcInit() { - var state = new CBCState(this.options.iv); - this._cbcState = state; -}; - -proto._update = function _update(inp, inOff, out, outOff) { - var state = this._cbcState; - var superProto = this.constructor.super_.prototype; +const MapHandler = { + get (target, key) { + const { context, objectId, path } = target + if (key === OBJECT_ID) return objectId + if (key === CHANGE) return context + if (key === STATE) return {actorId: context.actorId} + return context.getObjectField(path, objectId, key) + }, - var iv = state.iv; - if (this.type === 'encrypt') { - for (var i = 0; i < this.blockSize; i++) - iv[i] ^= inp[inOff + i]; + set (target, key, value) { + const { context, path, readonly } = target + if (Array.isArray(readonly) && readonly.indexOf(key) >= 0) { + throw new RangeError(`Object property "${key}" cannot be modified`) + } + context.setMapKey(path, key, value) + return true + }, - superProto._update.call(this, iv, 0, out, outOff); + deleteProperty (target, key) { + const { context, path, readonly } = target + if (Array.isArray(readonly) && readonly.indexOf(key) >= 0) { + throw new RangeError(`Object property "${key}" cannot be modified`) + } + context.deleteMapKey(path, key) + return true + }, - for (var i = 0; i < this.blockSize; i++) - iv[i] = out[outOff + i]; - } else { - superProto._update.call(this, inp, inOff, out, outOff); + has (target, key) { + const { context, objectId } = target + return [OBJECT_ID, CHANGE].includes(key) || (key in context.getObject(objectId)) + }, - for (var i = 0; i < this.blockSize; i++) - out[outOff + i] ^= iv[i]; + getOwnPropertyDescriptor (target, key) { + const { context, objectId } = target + const object = context.getObject(objectId) + if (key in object) { + return { + configurable: true, enumerable: true, + value: context.getObjectField(objectId, key) + } + } + }, - for (var i = 0; i < this.blockSize; i++) - iv[i] = inp[inOff + i]; + ownKeys (target) { + const { context, objectId } = target + return Object.keys(context.getObject(objectId)) } -}; - -},{"inherits":243,"minimalistic-assert":288}],173:[function(require,module,exports){ -'use strict'; - -var assert = require('minimalistic-assert'); - -function Cipher(options) { - this.options = options; - - this.type = this.options.type; - this.blockSize = 8; - this._init(); - - this.buffer = new Array(this.blockSize); - this.bufferOff = 0; } -module.exports = Cipher; - -Cipher.prototype._init = function _init() { - // Might be overrided -}; - -Cipher.prototype.update = function update(data) { - if (data.length === 0) - return []; - - if (this.type === 'decrypt') - return this._updateDecrypt(data); - else - return this._updateEncrypt(data); -}; -Cipher.prototype._buffer = function _buffer(data, off) { - // Append data to buffer - var min = Math.min(this.buffer.length - this.bufferOff, data.length - off); - for (var i = 0; i < min; i++) - this.buffer[this.bufferOff + i] = data[off + i]; - this.bufferOff += min; +const ListHandler = { + get (target, key) { + const [context, objectId, path] = target + if (key === Symbol.iterator) return context.getObject(objectId)[Symbol.iterator] + if (key === OBJECT_ID) return objectId + if (key === CHANGE) return context + if (key === 'length') return context.getObject(objectId).length + if (typeof key === 'string' && /^[0-9]+$/.test(key)) { + return context.getObjectField(path, objectId, parseListIndex(key)) + } + return listMethods(context, objectId, path)[key] + }, - // Shift next - return min; -}; + set (target, key, value) { + const [context, objectId, path] = target + if (key === 'length') { + if (typeof value !== 'number') { + throw new RangeError("Invalid array length") + } + const length = context.getObject(objectId).length + if (length > value) { + context.splice(path, value, length - value, []) + } else { + context.splice(path, length, 0, createArrayOfNulls(value - length)) + } + } else { + context.setListIndex(path, parseListIndex(key), value) + } + return true + }, -Cipher.prototype._flushBuffer = function _flushBuffer(out, off) { - this._update(this.buffer, 0, out, off); - this.bufferOff = 0; - return this.blockSize; -}; + deleteProperty (target, key) { + const [context, /* objectId */, path] = target + context.splice(path, parseListIndex(key), 1, []) + return true + }, -Cipher.prototype._updateEncrypt = function _updateEncrypt(data) { - var inputOff = 0; - var outputOff = 0; + has (target, key) { + const [context, objectId, /* path */] = target + if (typeof key === 'string' && /^[0-9]+$/.test(key)) { + return parseListIndex(key) < context.getObject(objectId).length + } + return ['length', OBJECT_ID, CHANGE].includes(key) + }, - var count = ((this.bufferOff + data.length) / this.blockSize) | 0; - var out = new Array(count * this.blockSize); + getOwnPropertyDescriptor (target, key) { + const [context, objectId, /* path */] = target + const object = context.getObject(objectId) - if (this.bufferOff !== 0) { - inputOff += this._buffer(data, inputOff); + if (key === 'length') return {writable: true, value: object.length} + if (key === OBJECT_ID) return {configurable: false, enumerable: false, value: objectId} - if (this.bufferOff === this.buffer.length) - outputOff += this._flushBuffer(out, outputOff); - } + if (typeof key === 'string' && /^[0-9]+$/.test(key)) { + const index = parseListIndex(key) + if (index < object.length) return { + configurable: true, enumerable: true, + value: context.getObjectField(objectId, index) + } + } + }, - // Write blocks - var max = data.length - ((data.length - inputOff) % this.blockSize); - for (; inputOff < max; inputOff += this.blockSize) { - this._update(data, inputOff, out, outputOff); - outputOff += this.blockSize; + ownKeys (target) { + const [context, objectId, /* path */] = target + const object = context.getObject(objectId) + let keys = ['length'] + for (let key of Object.keys(object)) keys.push(key) + return keys } +} - // Queue rest - for (; inputOff < data.length; inputOff++, this.bufferOff++) - this.buffer[this.bufferOff] = data[inputOff]; - - return out; -}; - -Cipher.prototype._updateDecrypt = function _updateDecrypt(data) { - var inputOff = 0; - var outputOff = 0; +function mapProxy(context, objectId, path, readonly) { + return new Proxy({context, objectId, path, readonly}, MapHandler) +} - var count = Math.ceil((this.bufferOff + data.length) / this.blockSize) - 1; - var out = new Array(count * this.blockSize); +function listProxy(context, objectId, path) { + return new Proxy([context, objectId, path], ListHandler) +} - // TODO(indutny): optimize it, this is far from optimal - for (; count > 0; count--) { - inputOff += this._buffer(data, inputOff); - outputOff += this._flushBuffer(out, outputOff); +/** + * Instantiates a proxy object for the given `objectId`. + * This function is added as a method to the context object by rootObjectProxy(). + * When it is called, `this` is the context object. + * `readonly` is a list of map property names that cannot be modified. + */ +function instantiateProxy(path, objectId, readonly) { + const object = this.getObject(objectId) + if (Array.isArray(object)) { + return listProxy(this, objectId, path) + } else if (object instanceof Text || object instanceof Table) { + return object.getWriteable(this, path) + } else { + return mapProxy(this, objectId, path, readonly) } +} - // Buffer rest of the input - inputOff += this._buffer(data, inputOff); - - return out; -}; - -Cipher.prototype.final = function final(buffer) { - var first; - if (buffer) - first = this.update(buffer); - - var last; - if (this.type === 'encrypt') - last = this._finalEncrypt(); - else - last = this._finalDecrypt(); - - if (first) - return first.concat(last); - else - return last; -}; +function rootObjectProxy(context) { + context.instantiateObject = instantiateProxy + return mapProxy(context, '_root', []) +} -Cipher.prototype._pad = function _pad(buffer, off) { - if (off === 0) - return false; +module.exports = { rootObjectProxy } - while (off < buffer.length) - buffer[off++] = 0; +},{"../src/common":101,"./constants":91,"./table":98,"./text":99}],98:[function(require,module,exports){ +const { OBJECT_ID, CONFLICTS } = require('./constants') +const { isObject, copyObject } = require('../src/common') - return true; -}; +function compareRows(properties, row1, row2) { + for (let prop of properties) { + if (row1[prop] === row2[prop]) continue -Cipher.prototype._finalEncrypt = function _finalEncrypt() { - if (!this._pad(this.buffer, this.bufferOff)) - return []; + if (typeof row1[prop] === 'number' && typeof row2[prop] === 'number') { + return row1[prop] - row2[prop] + } else { + const prop1 = '' + row1[prop], prop2 = '' + row2[prop] + if (prop1 === prop2) continue + if (prop1 < prop2) return -1; else return +1 + } + } + return 0 +} - var out = new Array(this.blockSize); - this._update(this.buffer, 0, out, 0); - return out; -}; -Cipher.prototype._unpad = function _unpad(buffer) { - return buffer; -}; +/** + * A relational-style unordered collection of records (rows). Each row is an + * object that maps column names to values. The set of rows is represented by + * a map from UUID to row object. + */ +class Table { + /** + * This constructor is used by application code when creating a new Table + * object within a change callback. + */ + constructor() { + this.entries = Object.freeze({}) + this.opIds = Object.freeze({}) + Object.freeze(this) + } -Cipher.prototype._finalDecrypt = function _finalDecrypt() { - assert.equal(this.bufferOff, this.blockSize, 'Not enough data to decrypt'); - var out = new Array(this.blockSize); - this._flushBuffer(out, 0); + /** + * Looks up a row in the table by its unique ID. + */ + byId(id) { + return this.entries[id] + } - return this._unpad(out); -}; + /** + * Returns an array containing the unique IDs of all rows in the table, in no + * particular order. + */ + get ids() { + return Object.keys(this.entries).filter(key => { + const entry = this.entries[key] + return isObject(entry) && entry.id === key + }) + } -},{"minimalistic-assert":288}],174:[function(require,module,exports){ -'use strict'; + /** + * Returns the number of rows in the table. + */ + get count() { + return this.ids.length + } -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); + /** + * Returns an array containing all of the rows in the table, in no particular + * order. + */ + get rows() { + return this.ids.map(id => this.byId(id)) + } -var utils = require('./utils'); -var Cipher = require('./cipher'); + /** + * The standard JavaScript `filter()` method, which passes each row to the + * callback function and returns all rows for which the it returns true. + */ + filter(callback, thisArg) { + return this.rows.filter(callback, thisArg) + } -function DESState() { - this.tmp = new Array(2); - this.keys = null; -} + /** + * The standard JavaScript `find()` method, which passes each row to the + * callback function and returns the first row for which it returns true. + */ + find(callback, thisArg) { + return this.rows.find(callback, thisArg) + } -function DES(options) { - Cipher.call(this, options); + /** + * The standard JavaScript `map()` method, which passes each row to the + * callback function and returns a list of its return values. + */ + map(callback, thisArg) { + return this.rows.map(callback, thisArg) + } - var state = new DESState(); - this._desState = state; + /** + * Returns the list of rows, sorted by one of the following: + * - If a function argument is given, it compares rows as per + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Description + * - If a string argument is given, it is interpreted as a column name and + * rows are sorted according to that column. + * - If an array of strings is given, it is interpreted as a list of column + * names, and rows are sorted lexicographically by those columns. + * - If no argument is given, it sorts by row ID by default. + */ + sort(arg) { + if (typeof arg === 'function') { + return this.rows.sort(arg) + } else if (typeof arg === 'string') { + return this.rows.sort((row1, row2) => compareRows([arg], row1, row2)) + } else if (Array.isArray(arg)) { + return this.rows.sort((row1, row2) => compareRows(arg, row1, row2)) + } else if (arg === undefined) { + return this.rows.sort((row1, row2) => compareRows(['id'], row1, row2)) + } else { + throw new TypeError(`Unsupported sorting argument: ${arg}`) + } + } - this.deriveKeys(state, options.key); -} -inherits(DES, Cipher); -module.exports = DES; + /** + * When iterating over a table, you get all rows in the table, in no + * particular order. + */ + [Symbol.iterator] () { + let rows = this.rows, index = -1 + return { + next () { + index += 1 + if (index < rows.length) { + return {done: false, value: rows[index]} + } else { + return {done: true} + } + } + } + } -DES.create = function create(options) { - return new DES(options); -}; + /** + * Returns a shallow clone of this object. This clone is used while applying + * a patch to the table, and `freeze()` is called on it when we have finished + * applying the patch. + */ + _clone() { + if (!this[OBJECT_ID]) { + throw new RangeError('clone() requires the objectId to be set') + } + return instantiateTable(this[OBJECT_ID], copyObject(this.entries), copyObject(this.opIds)) + } -var shiftTable = [ - 1, 1, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 1 -]; + /** + * Sets the entry with key `id` to `value`. `opId` is the ID of the operation + * performing this assignment. This method is for internal use only; it is + * not part of the public API of Automerge.Table. + */ + _set(id, value, opId) { + if (Object.isFrozen(this.entries)) { + throw new Error('A table can only be modified in a change function') + } + if (isObject(value) && !Array.isArray(value)) { + Object.defineProperty(value, 'id', {value: id, enumerable: true}) + } + this.entries[id] = value + this.opIds[id] = opId + } -DES.prototype.deriveKeys = function deriveKeys(state, key) { - state.keys = new Array(16 * 2); + /** + * Removes the row with unique ID `id` from the table. + */ + remove(id) { + if (Object.isFrozen(this.entries)) { + throw new Error('A table can only be modified in a change function') + } + delete this.entries[id] + delete this.opIds[id] + } - assert.equal(key.length, this.blockSize, 'Invalid key length'); + /** + * Makes this object immutable. This is called after a change has been made. + */ + _freeze() { + Object.freeze(this.entries) + Object.freeze(this.opIds) + Object.freeze(this) + } - var kL = utils.readUInt32BE(key, 0); - var kR = utils.readUInt32BE(key, 4); + /** + * Returns a writeable instance of this table. This instance is returned when + * the table is accessed within a change callback. `context` is the proxy + * context that keeps track of the mutations. + */ + getWriteable(context, path) { + if (!this[OBJECT_ID]) { + throw new RangeError('getWriteable() requires the objectId to be set') + } - utils.pc1(kL, kR, state.tmp, 0); - kL = state.tmp[0]; - kR = state.tmp[1]; - for (var i = 0; i < state.keys.length; i += 2) { - var shift = shiftTable[i >>> 1]; - kL = utils.r28shl(kL, shift); - kR = utils.r28shl(kR, shift); - utils.pc2(kL, kR, state.keys, i); + const instance = Object.create(WriteableTable.prototype) + instance[OBJECT_ID] = this[OBJECT_ID] + instance.context = context + instance.entries = this.entries + instance.opIds = this.opIds + instance.path = path + return instance } -}; -DES.prototype._update = function _update(inp, inOff, out, outOff) { - var state = this._desState; + /** + * Returns an object containing the table entries, indexed by objectID, + * for serializing an Automerge document to JSON. + */ + toJSON() { + const rows = {} + for (let id of this.ids) rows[id] = this.byId(id) + return rows + } +} - var l = utils.readUInt32BE(inp, inOff); - var r = utils.readUInt32BE(inp, inOff + 4); +/** + * An instance of this class is used when a table is accessed within a change + * callback. + */ +class WriteableTable extends Table { + /** + * Returns a proxied version of the row with ID `id`. This row object can be + * modified within a change callback. + */ + byId(id) { + if (isObject(this.entries[id]) && this.entries[id].id === id) { + const objectId = this.entries[id][OBJECT_ID] + const path = this.path.concat([{key: id, objectId}]) + return this.context.instantiateObject(path, objectId, ['id']) + } + } - // Initial Permutation - utils.ip(l, r, state.tmp, 0); - l = state.tmp[0]; - r = state.tmp[1]; + /** + * Adds a new row to the table. The row is given as a map from + * column name to value. Returns the objectId of the new row. + */ + add(row) { + return this.context.addTableRow(this.path, row) + } - if (this.type === 'encrypt') - this._encrypt(state, l, r, state.tmp, 0); - else - this._decrypt(state, l, r, state.tmp, 0); + /** + * Removes the row with ID `id` from the table. Throws an exception if the row + * does not exist in the table. + */ + remove(id) { + if (isObject(this.entries[id]) && this.entries[id].id === id) { + this.context.deleteTableRow(this.path, id, this.opIds[id]) + } else { + throw new RangeError(`There is no row with ID ${id} in this table`) + } + } +} - l = state.tmp[0]; - r = state.tmp[1]; +/** + * This function is used to instantiate a Table object in the context of + * applying a patch (see apply_patch.js). + */ +function instantiateTable(objectId, entries, opIds) { + const instance = Object.create(Table.prototype) + if (!objectId) { + throw new RangeError('instantiateTable requires an objectId to be given') + } + instance[OBJECT_ID] = objectId + instance[CONFLICTS] = Object.freeze({}) + instance.entries = entries || {} + instance.opIds = opIds || {} + return instance +} - utils.writeUInt32BE(out, l, outOff); - utils.writeUInt32BE(out, r, outOff + 4); -}; +module.exports = { Table, instantiateTable } -DES.prototype._pad = function _pad(buffer, off) { - var value = buffer.length - off; - for (var i = off; i < buffer.length; i++) - buffer[i] = value; +},{"../src/common":101,"./constants":91}],99:[function(require,module,exports){ +const { OBJECT_ID } = require('./constants') +const { isObject } = require('../src/common') - return true; -}; +class Text { + constructor (text) { + if (typeof text === 'string') { + const elems = [...text].map(value => ({value})) + return instantiateText(undefined, elems) // eslint-disable-line + } else if (Array.isArray(text)) { + const elems = text.map(value => ({value})) + return instantiateText(undefined, elems) // eslint-disable-line + } else if (text === undefined) { + return instantiateText(undefined, []) // eslint-disable-line + } else { + throw new TypeError(`Unsupported initial value for Text: ${text}`) + } + } -DES.prototype._unpad = function _unpad(buffer) { - var pad = buffer[buffer.length - 1]; - for (var i = buffer.length - pad; i < buffer.length; i++) - assert.equal(buffer[i], pad); + get length () { + return this.elems.length + } - return buffer.slice(0, buffer.length - pad); -}; + get (index) { + const value = this.elems[index].value + if (this.context && isObject(value)) { + const objectId = value[OBJECT_ID] + const path = this.path.concat([{key: index, objectId}]) + return this.context.instantiateObject(path, objectId) + } else { + return value + } + } -DES.prototype._encrypt = function _encrypt(state, lStart, rStart, out, off) { - var l = lStart; - var r = rStart; + getElemId (index) { + return this.elems[index].elemId + } - // Apply f() x16 times - for (var i = 0; i < state.keys.length; i += 2) { - var keyL = state.keys[i]; - var keyR = state.keys[i + 1]; + /** + * Iterates over the text elements character by character, including any + * inline objects. + */ + [Symbol.iterator] () { + let elems = this.elems, index = -1 + return { + next () { + index += 1 + if (index < elems.length) { + return {done: false, value: elems[index].value} + } else { + return {done: true} + } + } + } + } - // f(r, k) - utils.expand(r, state.tmp, 0); + /** + * Returns the content of the Text object as a simple string, ignoring any + * non-character elements. + */ + toString() { + // Concatting to a string is faster than creating an array and then + // .join()ing for small (<100KB) arrays. + // https://jsperf.com/join-vs-loop-w-type-test + let str = '' + for (const elem of this.elems) { + if (typeof elem.value === 'string') str += elem.value + } + return str + } - keyL ^= state.tmp[0]; - keyR ^= state.tmp[1]; - var s = utils.substitute(keyL, keyR); - var f = utils.permute(s); + /** + * Returns the content of the Text object as a sequence of strings, + * interleaved with non-character elements. + * + * For example, the value ['a', 'b', {x: 3}, 'c', 'd'] has spans: + * => ['ab', {x: 3}, 'cd'] + */ + toSpans() { + let spans = [] + let chars = '' + for (const elem of this.elems) { + if (typeof elem.value === 'string') { + chars += elem.value + } else { + if (chars.length > 0) { + spans.push(chars) + chars = '' + } + spans.push(elem.value) + } + } + if (chars.length > 0) { + spans.push(chars) + } + return spans + } - var t = r; - r = (l ^ f) >>> 0; - l = t; + /** + * Returns the content of the Text object as a simple string, so that the + * JSON serialization of an Automerge document represents text nicely. + */ + toJSON() { + return this.toString() } - // Reverse Initial Permutation - utils.rip(r, l, out, off); -}; + /** + * Returns a writeable instance of this object. This instance is returned when + * the text object is accessed within a change callback. `context` is the + * proxy context that keeps track of the mutations. + */ + getWriteable(context, path) { + if (!this[OBJECT_ID]) { + throw new RangeError('getWriteable() requires the objectId to be set') + } -DES.prototype._decrypt = function _decrypt(state, lStart, rStart, out, off) { - var l = rStart; - var r = lStart; + const instance = instantiateText(this[OBJECT_ID], this.elems) + instance.context = context + instance.path = path + return instance + } - // Apply f() x16 times - for (var i = state.keys.length - 2; i >= 0; i -= 2) { - var keyL = state.keys[i]; - var keyR = state.keys[i + 1]; + /** + * Updates the list item at position `index` to a new value `value`. + */ + set (index, value) { + if (this.context) { + this.context.setListIndex(this.path, index, value) + } else if (!this[OBJECT_ID]) { + this.elems[index].value = value + } else { + throw new TypeError('Automerge.Text object cannot be modified outside of a change block') + } + return this + } - // f(r, k) - utils.expand(l, state.tmp, 0); + /** + * Inserts new list items `values` starting at position `index`. + */ + insertAt(index, ...values) { + if (this.context) { + this.context.splice(this.path, index, 0, values) + } else if (!this[OBJECT_ID]) { + this.elems.splice(index, 0, ...values.map(value => ({value}))) + } else { + throw new TypeError('Automerge.Text object cannot be modified outside of a change block') + } + return this + } - keyL ^= state.tmp[0]; - keyR ^= state.tmp[1]; - var s = utils.substitute(keyL, keyR); - var f = utils.permute(s); + /** + * Deletes `numDelete` list items starting at position `index`. + * if `numDelete` is not given, one item is deleted. + */ + deleteAt(index, numDelete = 1) { + if (this.context) { + this.context.splice(this.path, index, numDelete, []) + } else if (!this[OBJECT_ID]) { + this.elems.splice(index, numDelete) + } else { + throw new TypeError('Automerge.Text object cannot be modified outside of a change block') + } + return this + } +} - var t = l; - l = (r ^ f) >>> 0; - r = t; +// Read-only methods that can delegate to the JavaScript built-in array +for (let method of ['concat', 'every', 'filter', 'find', 'findIndex', 'forEach', 'includes', + 'indexOf', 'join', 'lastIndexOf', 'map', 'reduce', 'reduceRight', + 'slice', 'some', 'toLocaleString']) { + Text.prototype[method] = function (...args) { + const array = [...this] + return array[method](...args) } +} - // Reverse Initial Permutation - utils.rip(l, r, out, off); -}; +function instantiateText(objectId, elems) { + const instance = Object.create(Text.prototype) + instance[OBJECT_ID] = objectId + instance.elems = elems + return instance +} -},{"./cipher":173,"./utils":176,"inherits":243,"minimalistic-assert":288}],175:[function(require,module,exports){ -'use strict'; +module.exports = { Text, instantiateText } -var assert = require('minimalistic-assert'); -var inherits = require('inherits'); +},{"../src/common":101,"./constants":91}],100:[function(require,module,exports){ +const uuid = require('./uuid') +const Frontend = require('../frontend') +const { OPTIONS } = require('../frontend/constants') +const { encodeChange, decodeChange } = require('../backend/columnar') +const { isObject } = require('./common') +let backend = require('../backend') // mutable: can be overridden with setDefaultBackend() -var Cipher = require('./cipher'); -var DES = require('./des'); +/** + * Automerge.* API + * The functions in this file constitute the publicly facing Automerge API which combines + * the features of the Frontend (a document interface) and the backend (CRDT operations) + */ -function EDEState(type, key) { - assert.equal(key.length, 24, 'Invalid key length'); +function init(options) { + if (typeof options === 'string') { + options = {actorId: options} + } else if (typeof options === 'undefined') { + options = {} + } else if (!isObject(options)) { + throw new TypeError(`Unsupported options for init(): ${options}`) + } + return Frontend.init(Object.assign({backend}, options)) +} - var k1 = key.slice(0, 8); - var k2 = key.slice(8, 16); - var k3 = key.slice(16, 24); +/** + * Returns a new document object initialized with the given state. + */ +function from(initialState, options) { + const changeOpts = {message: 'Initialization'} + return change(init(options), changeOpts, doc => Object.assign(doc, initialState)) +} - if (type === 'encrypt') { - this.ciphers = [ - DES.create({ type: 'encrypt', key: k1 }), - DES.create({ type: 'decrypt', key: k2 }), - DES.create({ type: 'encrypt', key: k3 }) - ]; - } else { - this.ciphers = [ - DES.create({ type: 'decrypt', key: k3 }), - DES.create({ type: 'encrypt', key: k2 }), - DES.create({ type: 'decrypt', key: k1 }) - ]; - } +function change(doc, options, callback) { + const [newDoc] = Frontend.change(doc, options, callback) + return newDoc +} + +function emptyChange(doc, options) { + const [newDoc] = Frontend.emptyChange(doc, options) + return newDoc +} + +function clone(doc, options = {}) { + const state = backend.clone(Frontend.getBackendState(doc, 'clone')) + return applyPatch(init(options), backend.getPatch(state), state, [], options) } -function EDE(options) { - Cipher.call(this, options); +function free(doc) { + backend.free(Frontend.getBackendState(doc, 'free')) +} - var state = new EDEState(this.type, this.options.key); - this._edeState = state; +function load(data, options = {}) { + const state = backend.load(data) + return applyPatch(init(options), backend.getPatch(state), state, [data], options) } -inherits(EDE, Cipher); -module.exports = EDE; +function save(doc) { + return backend.save(Frontend.getBackendState(doc, 'save')) +} -EDE.create = function create(options) { - return new EDE(options); -}; +function merge(localDoc, remoteDoc) { + const localState = Frontend.getBackendState(localDoc, 'merge') + const remoteState = Frontend.getBackendState(remoteDoc, 'merge', 'second') + if (Frontend.getActorId(localDoc) === Frontend.getActorId(remoteDoc)) { + throw new RangeError('Cannot merge an actor with itself') + } + const changes = backend.getChangesAdded(localState, remoteState) + const [updatedDoc] = applyChanges(localDoc, changes) + return updatedDoc +} -EDE.prototype._update = function _update(inp, inOff, out, outOff) { - var state = this._edeState; +function getChanges(oldDoc, newDoc) { + const oldState = Frontend.getBackendState(oldDoc, 'getChanges') + const newState = Frontend.getBackendState(newDoc, 'getChanges', 'second') + return backend.getChanges(newState, backend.getHeads(oldState)) +} - state.ciphers[0]._update(inp, inOff, out, outOff); - state.ciphers[1]._update(out, outOff, out, outOff); - state.ciphers[2]._update(out, outOff, out, outOff); -}; +function getAllChanges(doc) { + return backend.getAllChanges(Frontend.getBackendState(doc, 'getAllChanges')) +} -EDE.prototype._pad = DES.prototype._pad; -EDE.prototype._unpad = DES.prototype._unpad; +function applyPatch(doc, patch, backendState, changes, options) { + const newDoc = Frontend.applyPatch(doc, patch, backendState) + const patchCallback = options.patchCallback || doc[OPTIONS].patchCallback + if (patchCallback) { + patchCallback(patch, doc, newDoc, false, changes) + } + return newDoc +} -},{"./cipher":173,"./des":174,"inherits":243,"minimalistic-assert":288}],176:[function(require,module,exports){ -'use strict'; +function applyChanges(doc, changes, options = {}) { + const oldState = Frontend.getBackendState(doc, 'applyChanges') + const [newState, patch] = backend.applyChanges(oldState, changes) + return [applyPatch(doc, patch, newState, changes, options), patch] +} -exports.readUInt32BE = function readUInt32BE(bytes, off) { - var res = (bytes[0 + off] << 24) | - (bytes[1 + off] << 16) | - (bytes[2 + off] << 8) | - bytes[3 + off]; - return res >>> 0; -}; +function equals(val1, val2) { + if (!isObject(val1) || !isObject(val2)) return val1 === val2 + const keys1 = Object.keys(val1).sort(), keys2 = Object.keys(val2).sort() + if (keys1.length !== keys2.length) return false + for (let i = 0; i < keys1.length; i++) { + if (keys1[i] !== keys2[i]) return false + if (!equals(val1[keys1[i]], val2[keys2[i]])) return false + } + return true +} -exports.writeUInt32BE = function writeUInt32BE(bytes, value, off) { - bytes[0 + off] = value >>> 24; - bytes[1 + off] = (value >>> 16) & 0xff; - bytes[2 + off] = (value >>> 8) & 0xff; - bytes[3 + off] = value & 0xff; -}; +function getHistory(doc) { + const actor = Frontend.getActorId(doc) + const history = getAllChanges(doc) + return history.map((change, index) => ({ + get change () { + return decodeChange(change) + }, + get snapshot () { + const state = backend.loadChanges(backend.init(), history.slice(0, index + 1)) + return Frontend.applyPatch(init(actor), backend.getPatch(state), state) + } + }) + ) +} -exports.ip = function ip(inL, inR, out, off) { - var outL = 0; - var outR = 0; +function generateSyncMessage(doc, syncState) { + const state = Frontend.getBackendState(doc, 'generateSyncMessage') + return backend.generateSyncMessage(state, syncState) +} - for (var i = 6; i >= 0; i -= 2) { - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >>> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inL >>> (j + i)) & 1; - } - } +function receiveSyncMessage(doc, oldSyncState, message) { + const oldBackendState = Frontend.getBackendState(doc, 'receiveSyncMessage') + const [backendState, syncState, patch] = backend.receiveSyncMessage(oldBackendState, oldSyncState, message) + if (!patch) return [doc, syncState, patch] - for (var i = 6; i >= 0; i -= 2) { - for (var j = 1; j <= 25; j += 8) { - outR <<= 1; - outR |= (inR >>> (j + i)) & 1; - } - for (var j = 1; j <= 25; j += 8) { - outR <<= 1; - outR |= (inL >>> (j + i)) & 1; - } + // The patchCallback is passed as argument all changes that are applied. + // We get those from the sync message if a patchCallback is present. + let changes = null + if (doc[OPTIONS].patchCallback) { + changes = backend.decodeSyncMessage(message).changes } + return [applyPatch(doc, patch, backendState, changes, {}), syncState, patch] +} - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; +function initSyncState() { + return backend.initSyncState() +} -exports.rip = function rip(inL, inR, out, off) { - var outL = 0; - var outR = 0; +/** + * Replaces the default backend implementation with a different one. + * This allows you to switch to using the Rust/WebAssembly implementation. + */ +function setDefaultBackend(newBackend) { + backend = newBackend +} - for (var i = 0; i < 4; i++) { - for (var j = 24; j >= 0; j -= 8) { - outL <<= 1; - outL |= (inR >>> (j + i)) & 1; - outL <<= 1; - outL |= (inL >>> (j + i)) & 1; - } - } - for (var i = 4; i < 8; i++) { - for (var j = 24; j >= 0; j -= 8) { - outR <<= 1; - outR |= (inR >>> (j + i)) & 1; - outR <<= 1; - outR |= (inL >>> (j + i)) & 1; - } - } +module.exports = { + init, from, change, emptyChange, clone, free, + load, save, merge, getChanges, getAllChanges, applyChanges, + encodeChange, decodeChange, equals, getHistory, uuid, + Frontend, setDefaultBackend, generateSyncMessage, receiveSyncMessage, initSyncState, + get Backend() { return backend } +} - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; +for (let name of ['getObjectId', 'getObjectById', 'getActorId', + 'setActorId', 'getConflicts', 'getLastLocalChange', + 'Text', 'Table', 'Counter', 'Observable', 'Int', 'Uint', 'Float64']) { + module.exports[name] = Frontend[name] +} -exports.pc1 = function pc1(inL, inR, out, off) { - var outL = 0; - var outR = 0; +},{"../backend":86,"../backend/columnar":84,"../frontend":94,"../frontend/constants":91,"./common":101,"./uuid":102}],101:[function(require,module,exports){ +function isObject(obj) { + return typeof obj === 'object' && obj !== null +} - // 7, 15, 23, 31, 39, 47, 55, 63 - // 6, 14, 22, 30, 39, 47, 55, 63 - // 5, 13, 21, 29, 39, 47, 55, 63 - // 4, 12, 20, 28 - for (var i = 7; i >= 5; i--) { - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inL >> (j + i)) & 1; - } - } - for (var j = 0; j <= 24; j += 8) { - outL <<= 1; - outL |= (inR >> (j + i)) & 1; +/** + * Returns a shallow copy of the object `obj`. Faster than `Object.assign({}, obj)`. + * https://jsperf.com/cloning-large-objects/1 + */ +function copyObject(obj) { + if (!isObject(obj)) return {} + let copy = {} + for (let key of Object.keys(obj)) { + copy[key] = obj[key] } + return copy +} - // 1, 9, 17, 25, 33, 41, 49, 57 - // 2, 10, 18, 26, 34, 42, 50, 58 - // 3, 11, 19, 27, 35, 43, 51, 59 - // 36, 44, 52, 60 - for (var i = 1; i <= 3; i++) { - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inR >> (j + i)) & 1; - } - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inL >> (j + i)) & 1; - } - } - for (var j = 0; j <= 24; j += 8) { - outR <<= 1; - outR |= (inL >> (j + i)) & 1; +/** + * Takes a string in the form that is used to identify operations (a counter concatenated + * with an actor ID, separated by an `@` sign) and returns an object `{counter, actorId}`. + */ +function parseOpId(opId) { + const match = /^(\d+)@(.*)$/.exec(opId || '') + if (!match) { + throw new RangeError(`Not a valid opId: ${opId}`) } + return {counter: parseInt(match[1], 10), actorId: match[2]} +} - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; - -exports.r28shl = function r28shl(num, shift) { - return ((num << shift) & 0xfffffff) | (num >>> (28 - shift)); -}; - -var pc2table = [ - // inL => outL - 14, 11, 17, 4, 27, 23, 25, 0, - 13, 22, 7, 18, 5, 9, 16, 24, - 2, 20, 12, 21, 1, 8, 15, 26, - - // inR => outR - 15, 4, 25, 19, 9, 1, 26, 16, - 5, 11, 23, 8, 12, 7, 17, 0, - 22, 3, 10, 14, 6, 20, 27, 24 -]; - -exports.pc2 = function pc2(inL, inR, out, off) { - var outL = 0; - var outR = 0; - - var len = pc2table.length >>> 1; - for (var i = 0; i < len; i++) { - outL <<= 1; - outL |= (inL >>> pc2table[i]) & 0x1; +/** + * Returns true if the two byte arrays contain the same data, false if not. + */ +function equalBytes(array1, array2) { + if (!(array1 instanceof Uint8Array) || !(array2 instanceof Uint8Array)) { + throw new TypeError('equalBytes can only compare Uint8Arrays') } - for (var i = len; i < pc2table.length; i++) { - outR <<= 1; - outR |= (inR >>> pc2table[i]) & 0x1; + if (array1.byteLength !== array2.byteLength) return false + for (let i = 0; i < array1.byteLength; i++) { + if (array1[i] !== array2[i]) return false } + return true +} - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; +/** + * Creates an array containing the value `null` repeated `length` times. + */ +function createArrayOfNulls(length) { + const array = new Array(length) + for (let i = 0; i < length; i++) array[i] = null + return array +} -exports.expand = function expand(r, out, off) { - var outL = 0; - var outR = 0; +module.exports = { + isObject, copyObject, parseOpId, equalBytes, createArrayOfNulls +} - outL = ((r & 1) << 5) | (r >>> 27); - for (var i = 23; i >= 15; i -= 4) { - outL <<= 6; - outL |= (r >>> i) & 0x3f; - } - for (var i = 11; i >= 3; i -= 4) { - outR |= (r >>> i) & 0x3f; - outR <<= 6; - } - outR |= ((r & 0x1f) << 1) | (r >>> 31); +},{}],102:[function(require,module,exports){ +const { v4: uuid } = require('uuid') - out[off + 0] = outL >>> 0; - out[off + 1] = outR >>> 0; -}; +function defaultFactory() { + return uuid().replace(/-/g, '') +} -var sTable = [ - 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, - 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, - 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, - 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13, +let factory = defaultFactory - 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, - 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, - 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, - 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9, +function makeUuid() { + return factory() +} - 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, - 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, - 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, - 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12, +makeUuid.setFactory = newFactory => { factory = newFactory } +makeUuid.reset = () => { factory = defaultFactory } - 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, - 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, - 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, - 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14, +module.exports = makeUuid - 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, - 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, - 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, - 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3, +},{"uuid":202}],103:[function(require,module,exports){ +var r; - 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, - 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, - 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, - 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13, +module.exports = function rand(len) { + if (!r) + r = new Rand(null); - 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, - 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, - 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, - 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12, + return r.generate(len); +}; - 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, - 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, - 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, - 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 -]; +function Rand(rand) { + this.rand = rand; +} +module.exports.Rand = Rand; -exports.substitute = function substitute(inL, inR) { - var out = 0; - for (var i = 0; i < 4; i++) { - var b = (inL >>> (18 - i * 6)) & 0x3f; - var sb = sTable[i * 0x40 + b]; +Rand.prototype.generate = function generate(len) { + return this._rand(len); +}; - out <<= 4; - out |= sb; - } - for (var i = 0; i < 4; i++) { - var b = (inR >>> (18 - i * 6)) & 0x3f; - var sb = sTable[4 * 0x40 + i * 0x40 + b]; +// Emulate crypto API using randy +Rand.prototype._rand = function _rand(n) { + if (this.rand.getBytes) + return this.rand.getBytes(n); - out <<= 4; - out |= sb; - } - return out >>> 0; + var res = new Uint8Array(n); + for (var i = 0; i < res.length; i++) + res[i] = this.rand.getByte(); + return res; }; -var permuteTable = [ - 16, 25, 12, 11, 3, 20, 4, 15, 31, 17, 9, 6, 27, 14, 1, 22, - 30, 24, 8, 18, 0, 5, 29, 23, 13, 19, 2, 26, 10, 21, 28, 7 -]; +if (typeof self === 'object') { + if (self.crypto && self.crypto.getRandomValues) { + // Modern browsers + Rand.prototype._rand = function _rand(n) { + var arr = new Uint8Array(n); + self.crypto.getRandomValues(arr); + return arr; + }; + } else if (self.msCrypto && self.msCrypto.getRandomValues) { + // IE + Rand.prototype._rand = function _rand(n) { + var arr = new Uint8Array(n); + self.msCrypto.getRandomValues(arr); + return arr; + }; -exports.permute = function permute(num) { - var out = 0; - for (var i = 0; i < permuteTable.length; i++) { - out <<= 1; - out |= (num >>> permuteTable[i]) & 0x1; + // Safari's WebWorkers do not have `crypto` + } else if (typeof window === 'object') { + // Old junk + Rand.prototype._rand = function() { + throw new Error('Not implemented yet'); + }; } - return out >>> 0; -}; +} else { + // Node.js or Web worker with no crypto support + try { + var crypto = require('crypto'); + if (typeof crypto.randomBytes !== 'function') + throw new Error('Not supported'); -exports.padSplit = function padSplit(num, size, group) { - var str = num.toString(2); - while (str.length < size) - str = '0' + str; + Rand.prototype._rand = function _rand(n) { + return crypto.randomBytes(n); + }; + } catch (e) { + } +} - var out = []; - for (var i = 0; i < size; i += group) - out.push(str.slice(i, i + group)); - return out.join(' '); -}; +},{"crypto":292}],104:[function(require,module,exports){ +'use strict' -},{}],177:[function(require,module,exports){ -(function (Buffer){(function (){ -var generatePrime = require('./lib/generatePrime') -var primes = require('./lib/primes.json') +var queueTick = require('queue-tick') -var DH = require('./lib/dh') +exports.fromCallback = function (callback, symbol) { + if (callback === undefined) { + var promise = new Promise(function (resolve, reject) { + callback = function (err, res) { + if (err) reject(err) + else resolve(res) + } + }) -function getDiffieHellman (mod) { - var prime = new Buffer(primes[mod].prime, 'hex') - var gen = new Buffer(primes[mod].gen, 'hex') + callback[symbol !== undefined ? symbol : 'promise'] = promise + } else if (typeof callback !== 'function') { + throw new TypeError('Callback must be a function') + } - return new DH(prime, gen) + return callback } -var ENCODINGS = { - 'binary': true, 'hex': true, 'base64': true +exports.fromPromise = function (promise, callback) { + if (callback === undefined) return promise + + promise + .then(function (res) { queueTick(() => callback(null, res)) }) + .catch(function (err) { queueTick(() => callback(err)) }) } -function createDiffieHellman (prime, enc, generator, genc) { - if (Buffer.isBuffer(enc) || ENCODINGS[enc] === undefined) { - return createDiffieHellman(prime, 'binary', enc, generator) +},{"queue-tick":182}],105:[function(require,module,exports){ +'use strict' + +const { AbstractChainedBatch } = require('abstract-leveldown') +const kOperations = Symbol('operations') + +module.exports = class DeferredChainedBatch extends AbstractChainedBatch { + constructor (db) { + super(db) + this[kOperations] = [] } - enc = enc || 'binary' - genc = genc || 'binary' - generator = generator || new Buffer([2]) + _put (key, value, options) { + this[kOperations].push({ ...options, type: 'put', key, value }) + } - if (!Buffer.isBuffer(generator)) { - generator = new Buffer(generator, genc) + _del (key, options) { + this[kOperations].push({ ...options, type: 'del', key }) } - if (typeof prime === 'number') { - return new DH(generatePrime(prime, generator), generator, true) + _clear () { + this[kOperations] = [] } - if (!Buffer.isBuffer(prime)) { - prime = new Buffer(prime, enc) + _write (options, callback) { + // AbstractChainedBatch would call _batch(), we call batch() + this.db.batch(this[kOperations], options, callback) } +} + +},{"abstract-leveldown":80}],106:[function(require,module,exports){ +'use strict' - return new DH(prime, generator, true) -} +const { AbstractIterator } = require('abstract-leveldown') +const inherits = require('inherits') +const getCallback = require('./util').getCallback -exports.DiffieHellmanGroup = exports.createDiffieHellmanGroup = exports.getDiffieHellman = getDiffieHellman -exports.createDiffieHellman = exports.DiffieHellman = createDiffieHellman +const kOptions = Symbol('options') +const kIterator = Symbol('iterator') +const kOperations = Symbol('operations') +const kPromise = Symbol('promise') -}).call(this)}).call(this,require("buffer").Buffer) -},{"./lib/dh":178,"./lib/generatePrime":179,"./lib/primes.json":180,"buffer":135}],178:[function(require,module,exports){ -(function (Buffer){(function (){ -var BN = require('bn.js'); -var MillerRabin = require('miller-rabin'); -var millerRabin = new MillerRabin(); -var TWENTYFOUR = new BN(24); -var ELEVEN = new BN(11); -var TEN = new BN(10); -var THREE = new BN(3); -var SEVEN = new BN(7); -var primes = require('./generatePrime'); -var randomBytes = require('randombytes'); -module.exports = DH; +function DeferredIterator (db, options) { + AbstractIterator.call(this, db) -function setPublicKey(pub, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(pub)) { - pub = new Buffer(pub, enc); - } - this._pub = new BN(pub); - return this; + this[kOptions] = options + this[kIterator] = null + this[kOperations] = [] } -function setPrivateKey(priv, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(priv)) { - priv = new Buffer(priv, enc); +inherits(DeferredIterator, AbstractIterator) + +DeferredIterator.prototype.setDb = function (db) { + this[kIterator] = db.iterator(this[kOptions]) + + for (const op of this[kOperations].splice(0, this[kOperations].length)) { + this[kIterator][op.method](...op.args) } - this._priv = new BN(priv); - return this; } -var primeCache = {}; -function checkPrime(prime, generator) { - var gen = generator.toString('hex'); - var hex = [gen, prime.toString(16)].join('_'); - if (hex in primeCache) { - return primeCache[hex]; +DeferredIterator.prototype.next = function (...args) { + if (this.db.status === 'open') { + return this[kIterator].next(...args) } - var error = 0; - - if (prime.isEven() || - !primes.simpleSieve || - !primes.fermatTest(prime) || - !millerRabin.test(prime)) { - //not a prime so +1 - error += 1; - if (gen === '02' || gen === '05') { - // we'd be able to check the generator - // it would fail so +8 - error += 8; + const callback = getCallback(args, kPromise, function map (key, value) { + if (key === undefined && value === undefined) { + return undefined } else { - //we wouldn't be able to test the generator - // so +4 - error += 4; + return [key, value] } - primeCache[hex] = error; - return error; - } - if (!millerRabin.test(prime.shrn(1))) { - //not a safe prime - error += 2; - } - var rem; - switch (gen) { - case '02': - if (prime.mod(TWENTYFOUR).cmp(ELEVEN)) { - // unsuidable generator - error += 8; - } - break; - case '05': - rem = prime.mod(TEN); - if (rem.cmp(THREE) && rem.cmp(SEVEN)) { - // prime mod 10 needs to equal 3 or 7 - error += 8; - } - break; - default: - error += 4; + }) + + if (this.db.status === 'opening') { + this[kOperations].push({ method: 'next', args }) + } else { + this._nextTick(callback, new Error('Database is not open')) } - primeCache[hex] = error; - return error; + + return callback[kPromise] || this } -function DH(prime, generator, malleable) { - this.setGenerator(generator); - this.__prime = new BN(prime); - this._prime = BN.mont(this.__prime); - this._primeLen = prime.length; - this._pub = undefined; - this._priv = undefined; - this._primeCode = undefined; - if (malleable) { - this.setPublicKey = setPublicKey; - this.setPrivateKey = setPrivateKey; +DeferredIterator.prototype.seek = function (...args) { + if (this.db.status === 'open') { + this[kIterator].seek(...args) + } else if (this.db.status === 'opening') { + this[kOperations].push({ method: 'seek', args }) } else { - this._primeCode = 8; + throw new Error('Database is not open') } } -Object.defineProperty(DH.prototype, 'verifyError', { - enumerable: true, - get: function () { - if (typeof this._primeCode !== 'number') { - this._primeCode = checkPrime(this.__prime, this.__gen); - } - return this._primeCode; + +DeferredIterator.prototype.end = function (...args) { + if (this.db.status === 'open') { + return this[kIterator].end(...args) } -}); -DH.prototype.generateKeys = function () { - if (!this._priv) { - this._priv = new BN(randomBytes(this._primeLen)); + + const callback = getCallback(args, kPromise) + + if (this.db.status === 'opening') { + this[kOperations].push({ method: 'end', args }) + } else { + this._nextTick(callback, new Error('Database is not open')) } - this._pub = this._gen.toRed(this._prime).redPow(this._priv).fromRed(); - return this.getPublicKey(); -}; -DH.prototype.computeSecret = function (other) { - other = new BN(other); - other = other.toRed(this._prime); - var secret = other.redPow(this._priv).fromRed(); - var out = new Buffer(secret.toArray()); - var prime = this.getPrime(); - if (out.length < prime.length) { - var front = new Buffer(prime.length - out.length); - front.fill(0); - out = Buffer.concat([front, out]); + return callback[kPromise] || this +} + +for (const method of ['next', 'seek', 'end']) { + DeferredIterator.prototype['_' + method] = function () { + /* istanbul ignore next: assertion */ + throw new Error('Did not expect private method to be called: ' + method) } - return out; -}; +} -DH.prototype.getPublicKey = function getPublicKey(enc) { - return formatReturnValue(this._pub, enc); -}; +module.exports = DeferredIterator -DH.prototype.getPrivateKey = function getPrivateKey(enc) { - return formatReturnValue(this._priv, enc); -}; +},{"./util":108,"abstract-leveldown":80,"inherits":141}],107:[function(require,module,exports){ +'use strict' -DH.prototype.getPrime = function (enc) { - return formatReturnValue(this.__prime, enc); -}; +const { AbstractLevelDOWN } = require('abstract-leveldown') +const inherits = require('inherits') +const DeferredIterator = require('./deferred-iterator') +const DeferredChainedBatch = require('./deferred-chained-batch') +const getCallback = require('./util').getCallback -DH.prototype.getGenerator = function (enc) { - return formatReturnValue(this._gen, enc); -}; +const deferrables = ['put', 'get', 'getMany', 'del', 'batch', 'clear'] +const optionalDeferrables = ['approximateSize', 'compactRange'] -DH.prototype.setGenerator = function (gen, enc) { - enc = enc || 'utf8'; - if (!Buffer.isBuffer(gen)) { - gen = new Buffer(gen, enc); - } - this.__gen = gen; - this._gen = new BN(gen); - return this; -}; +const kInnerDb = Symbol('innerDb') +const kOperations = Symbol('operations') +const kPromise = Symbol('promise') -function formatReturnValue(bn, enc) { - var buf = new Buffer(bn.toArray()); - if (!enc) { - return buf; - } else { - return buf.toString(enc); +function DeferredLevelDOWN (db) { + AbstractLevelDOWN.call(this, db.supports || {}) + + // TODO (future major): remove this fallback; db must have manifest that + // declares approximateSize and compactRange in additionalMethods. + for (const m of optionalDeferrables) { + if (typeof db[m] === 'function' && !this.supports.additionalMethods[m]) { + this.supports.additionalMethods[m] = true + } } -} -}).call(this)}).call(this,require("buffer").Buffer) -},{"./generatePrime":179,"bn.js":181,"buffer":135,"miller-rabin":286,"randombytes":386}],179:[function(require,module,exports){ -var randomBytes = require('randombytes'); -module.exports = findPrime; -findPrime.simpleSieve = simpleSieve; -findPrime.fermatTest = fermatTest; -var BN = require('bn.js'); -var TWENTYFOUR = new BN(24); -var MillerRabin = require('miller-rabin'); -var millerRabin = new MillerRabin(); -var ONE = new BN(1); -var TWO = new BN(2); -var FIVE = new BN(5); -var SIXTEEN = new BN(16); -var EIGHT = new BN(8); -var TEN = new BN(10); -var THREE = new BN(3); -var SEVEN = new BN(7); -var ELEVEN = new BN(11); -var FOUR = new BN(4); -var TWELVE = new BN(12); -var primes = null; + this[kInnerDb] = db + this[kOperations] = [] -function _getPrimes() { - if (primes !== null) - return primes; + implement(this) +} - var limit = 0x100000; - var res = []; - res[0] = 2; - for (var i = 1, k = 3; k < limit; k += 2) { - var sqrt = Math.ceil(Math.sqrt(k)); - for (var j = 0; j < i && res[j] <= sqrt; j++) - if (k % res[j] === 0) - break; +inherits(DeferredLevelDOWN, AbstractLevelDOWN) - if (i !== j && res[j] <= sqrt) - continue; +DeferredLevelDOWN.prototype.type = 'deferred-leveldown' - res[i++] = k; +// Backwards compatibility for reachdown and subleveldown +Object.defineProperty(DeferredLevelDOWN.prototype, '_db', { + enumerable: true, + get () { + return this[kInnerDb] } - primes = res; - return res; -} +}) -function simpleSieve(p) { - var primes = _getPrimes(); +DeferredLevelDOWN.prototype._open = function (options, callback) { + const onopen = (err) => { + if (err || this[kInnerDb].status !== 'open') { + // TODO: reject scheduled operations + return callback(err || new Error('Database is not open')) + } - for (var i = 0; i < primes.length; i++) - if (p.modn(primes[i]) === 0) { - if (p.cmpn(primes[i]) === 0) { - return true; + const operations = this[kOperations] + this[kOperations] = [] + + for (const op of operations) { + if (op.iterator) { + op.iterator.setDb(this[kInnerDb]) } else { - return false; + this[kInnerDb][op.method](...op.args) } } - return true; + /* istanbul ignore if: assertion */ + if (this[kOperations].length > 0) { + throw new Error('Did not expect further operations') + } + + callback() + } + + if (this[kInnerDb].status === 'new' || this[kInnerDb].status === 'closed') { + this[kInnerDb].open(options, onopen) + } else { + this._nextTick(onopen) + } } -function fermatTest(p) { - var red = BN.mont(p); - return TWO.toRed(red).redPow(p.subn(1)).fromRed().cmpn(1) === 0; +DeferredLevelDOWN.prototype._close = function (callback) { + this[kInnerDb].close(callback) } -function findPrime(bits, gen) { - if (bits < 16) { - // this is what openssl does - if (gen === 2 || gen === 5) { - return new BN([0x8c, 0x7b]); - } else { - return new BN([0x8c, 0x27]); - } - } - gen = new BN(gen); +DeferredLevelDOWN.prototype._isOperational = function () { + return this.status === 'opening' +} - var num, n2; +function implement (self) { + const additionalMethods = Object.keys(self.supports.additionalMethods) - while (true) { - num = new BN(randomBytes(Math.ceil(bits / 8))); - while (num.bitLength() > bits) { - num.ishrn(1); - } - if (num.isEven()) { - num.iadd(ONE); - } - if (!num.testn(1)) { - num.iadd(TWO); - } - if (!gen.cmp(TWO)) { - while (num.mod(TWENTYFOUR).cmp(ELEVEN)) { - num.iadd(FOUR); + for (const method of deferrables.concat(additionalMethods)) { + // Override the public rather than private methods to cover cases where abstract-leveldown + // has a fast-path like on db.batch([]) which bypasses _batch() because the array is empty. + self[method] = function (...args) { + if (method === 'batch' && args.length === 0) { + return new DeferredChainedBatch(this) + } else if (this.status === 'open') { + return this[kInnerDb][method](...args) } - } else if (!gen.cmp(FIVE)) { - while (num.mod(TEN).cmp(THREE)) { - num.iadd(FOUR); + + const callback = getCallback(args, kPromise) + + if (this.status === 'opening') { + this[kOperations].push({ method, args }) + } else { + this._nextTick(callback, new Error('Database is not open')) } + + return callback[kPromise] } - n2 = num.shrn(1); - if (simpleSieve(n2) && simpleSieve(num) && - fermatTest(n2) && fermatTest(num) && - millerRabin.test(n2) && millerRabin.test(num)) { - return num; + } + + self.iterator = function (options) { + if (this.status === 'open') { + return this[kInnerDb].iterator(options) + } else if (this.status === 'opening') { + const iterator = new DeferredIterator(this, options) + this[kOperations].push({ iterator }) + return iterator + } else { + throw new Error('Database is not open') } } + for (const method of deferrables.concat(['iterator'])) { + self['_' + method] = function () { + /* istanbul ignore next: assertion */ + throw new Error('Did not expect private method to be called: ' + method) + } + } } -},{"bn.js":181,"miller-rabin":286,"randombytes":386}],180:[function(require,module,exports){ -module.exports={ - "modp1": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff" - }, - "modp2": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff" - }, - "modp5": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff" - }, - "modp14": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff" - }, - "modp15": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a93ad2caffffffffffffffff" - }, - "modp16": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c934063199ffffffffffffffff" - }, - "modp17": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dcc4024ffffffffffffffff" - }, - "modp18": { - "gen": "02", - "prime": "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dbe115974a3926f12fee5e438777cb6a932df8cd8bec4d073b931ba3bc832b68d9dd300741fa7bf8afc47ed2576f6936ba424663aab639c5ae4f5683423b4742bf1c978238f16cbe39d652de3fdb8befc848ad922222e04a4037c0713eb57a81a23f0c73473fc646cea306b4bcbc8862f8385ddfa9d4b7fa2c087e879683303ed5bdd3a062b3cf5b3a278a66d2a13f83f44f82ddf310ee074ab6a364597e899a0255dc164f31cc50846851df9ab48195ded7ea1b1d510bd7ee74d73faf36bc31ecfa268359046f4eb879f924009438b481c6cd7889a002ed5ee382bc9190da6fc026e479558e4475677e9aa9e3050e2765694dfc81f56e880b96e7160c980dd98edd3dfffffffffffffffff" - } +module.exports = DeferredLevelDOWN +module.exports.DeferredIterator = DeferredIterator + +},{"./deferred-chained-batch":105,"./deferred-iterator":106,"./util":108,"abstract-leveldown":80,"inherits":141}],108:[function(require,module,exports){ +'use strict' + +exports.getCallback = function (args, symbol, map) { + let callback = args[args.length - 1] + + if (typeof callback !== 'function') { + const promise = new Promise((resolve, reject) => { + args.push(callback = function (err, ...results) { + if (err) reject(err) + else resolve(map ? map(...results) : results[0]) + }) + }) + + callback[symbol] = promise + } + + return callback } -},{}],181:[function(require,module,exports){ -arguments[4][45][0].apply(exports,arguments) -},{"buffer":91,"dup":45}],182:[function(require,module,exports){ + +},{}],109:[function(require,module,exports){ 'use strict'; var elliptic = exports; @@ -35108,7 +12817,7 @@ elliptic.curves = require('./elliptic/curves'); elliptic.ec = require('./elliptic/ec'); elliptic.eddsa = require('./elliptic/eddsa'); -},{"../package.json":198,"./elliptic/curve":185,"./elliptic/curves":188,"./elliptic/ec":189,"./elliptic/eddsa":192,"./elliptic/utils":196,"brorand":90}],183:[function(require,module,exports){ +},{"../package.json":125,"./elliptic/curve":112,"./elliptic/curves":115,"./elliptic/ec":116,"./elliptic/eddsa":119,"./elliptic/utils":123,"brorand":103}],110:[function(require,module,exports){ 'use strict'; var BN = require('bn.js'); @@ -35491,7 +13200,7 @@ BasePoint.prototype.dblp = function dblp(k) { return r; }; -},{"../utils":196,"bn.js":197}],184:[function(require,module,exports){ +},{"../utils":123,"bn.js":124}],111:[function(require,module,exports){ 'use strict'; var utils = require('../utils'); @@ -35928,7 +13637,7 @@ Point.prototype.eqXToP = function eqXToP(x) { Point.prototype.toP = Point.prototype.normalize; Point.prototype.mixedAdd = Point.prototype.add; -},{"../utils":196,"./base":183,"bn.js":197,"inherits":243}],185:[function(require,module,exports){ +},{"../utils":123,"./base":110,"bn.js":124,"inherits":141}],112:[function(require,module,exports){ 'use strict'; var curve = exports; @@ -35938,7 +13647,7 @@ curve.short = require('./short'); curve.mont = require('./mont'); curve.edwards = require('./edwards'); -},{"./base":183,"./edwards":184,"./mont":186,"./short":187}],186:[function(require,module,exports){ +},{"./base":110,"./edwards":111,"./mont":113,"./short":114}],113:[function(require,module,exports){ 'use strict'; var BN = require('bn.js'); @@ -36118,7 +13827,7 @@ Point.prototype.getX = function getX() { return this.x.fromRed(); }; -},{"../utils":196,"./base":183,"bn.js":197,"inherits":243}],187:[function(require,module,exports){ +},{"../utils":123,"./base":110,"bn.js":124,"inherits":141}],114:[function(require,module,exports){ 'use strict'; var utils = require('../utils'); @@ -37058,7 +14767,7 @@ JPoint.prototype.isInfinity = function isInfinity() { return this.z.cmpn(0) === 0; }; -},{"../utils":196,"./base":183,"bn.js":197,"inherits":243}],188:[function(require,module,exports){ +},{"../utils":123,"./base":110,"bn.js":124,"inherits":141}],115:[function(require,module,exports){ 'use strict'; var curves = exports; @@ -37266,7 +14975,7 @@ defineCurve('secp256k1', { ], }); -},{"./curve":185,"./precomputed/secp256k1":195,"./utils":196,"hash.js":229}],189:[function(require,module,exports){ +},{"./curve":112,"./precomputed/secp256k1":122,"./utils":123,"hash.js":128}],116:[function(require,module,exports){ 'use strict'; var BN = require('bn.js'); @@ -37511,7 +15220,7 @@ EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) { throw new Error('Unable to find valid recovery factor'); }; -},{"../curves":188,"../utils":196,"./key":190,"./signature":191,"bn.js":197,"brorand":90,"hmac-drbg":241}],190:[function(require,module,exports){ +},{"../curves":115,"../utils":123,"./key":117,"./signature":118,"bn.js":124,"brorand":103,"hmac-drbg":140}],117:[function(require,module,exports){ 'use strict'; var BN = require('bn.js'); @@ -37634,7 +15343,7 @@ KeyPair.prototype.inspect = function inspect() { ' pub: ' + (this.pub && this.pub.inspect()) + ' >'; }; -},{"../utils":196,"bn.js":197}],191:[function(require,module,exports){ +},{"../utils":123,"bn.js":124}],118:[function(require,module,exports){ 'use strict'; var BN = require('bn.js'); @@ -37802,7 +15511,7 @@ Signature.prototype.toDER = function toDER(enc) { return utils.encode(res, enc); }; -},{"../utils":196,"bn.js":197}],192:[function(require,module,exports){ +},{"../utils":123,"bn.js":124}],119:[function(require,module,exports){ 'use strict'; var hash = require('hash.js'); @@ -37922,7 +15631,7 @@ EDDSA.prototype.isPoint = function isPoint(val) { return val instanceof this.pointClass; }; -},{"../curves":188,"../utils":196,"./key":193,"./signature":194,"hash.js":229}],193:[function(require,module,exports){ +},{"../curves":115,"../utils":123,"./key":120,"./signature":121,"hash.js":128}],120:[function(require,module,exports){ 'use strict'; var utils = require('../utils'); @@ -38019,7 +15728,7 @@ KeyPair.prototype.getPublic = function getPublic(enc) { module.exports = KeyPair; -},{"../utils":196}],194:[function(require,module,exports){ +},{"../utils":123}],121:[function(require,module,exports){ 'use strict'; var BN = require('bn.js'); @@ -38086,7 +15795,7 @@ Signature.prototype.toHex = function toHex() { module.exports = Signature; -},{"../utils":196,"bn.js":197}],195:[function(require,module,exports){ +},{"../utils":123,"bn.js":124}],122:[function(require,module,exports){ module.exports = { doubles: { step: 4, @@ -38868,4125 +16577,33435 @@ module.exports = { }, }; -},{}],196:[function(require,module,exports){ +},{}],123:[function(require,module,exports){ +'use strict'; + +var utils = exports; +var BN = require('bn.js'); +var minAssert = require('minimalistic-assert'); +var minUtils = require('minimalistic-crypto-utils'); + +utils.assert = minAssert; +utils.toArray = minUtils.toArray; +utils.zero2 = minUtils.zero2; +utils.toHex = minUtils.toHex; +utils.encode = minUtils.encode; + +// Represent num in a w-NAF form +function getNAF(num, w, bits) { + var naf = new Array(Math.max(num.bitLength(), bits) + 1); + naf.fill(0); + + var ws = 1 << (w + 1); + var k = num.clone(); + + for (var i = 0; i < naf.length; i++) { + var z; + var mod = k.andln(ws - 1); + if (k.isOdd()) { + if (mod > (ws >> 1) - 1) + z = (ws >> 1) - mod; + else + z = mod; + k.isubn(z); + } else { + z = 0; + } + + naf[i] = z; + k.iushrn(1); + } + + return naf; +} +utils.getNAF = getNAF; + +// Represent k1, k2 in a Joint Sparse Form +function getJSF(k1, k2) { + var jsf = [ + [], + [], + ]; + + k1 = k1.clone(); + k2 = k2.clone(); + var d1 = 0; + var d2 = 0; + var m8; + while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) { + // First phase + var m14 = (k1.andln(3) + d1) & 3; + var m24 = (k2.andln(3) + d2) & 3; + if (m14 === 3) + m14 = -1; + if (m24 === 3) + m24 = -1; + var u1; + if ((m14 & 1) === 0) { + u1 = 0; + } else { + m8 = (k1.andln(7) + d1) & 7; + if ((m8 === 3 || m8 === 5) && m24 === 2) + u1 = -m14; + else + u1 = m14; + } + jsf[0].push(u1); + + var u2; + if ((m24 & 1) === 0) { + u2 = 0; + } else { + m8 = (k2.andln(7) + d2) & 7; + if ((m8 === 3 || m8 === 5) && m14 === 2) + u2 = -m24; + else + u2 = m24; + } + jsf[1].push(u2); + + // Second phase + if (2 * d1 === u1 + 1) + d1 = 1 - d1; + if (2 * d2 === u2 + 1) + d2 = 1 - d2; + k1.iushrn(1); + k2.iushrn(1); + } + + return jsf; +} +utils.getJSF = getJSF; + +function cachedProperty(obj, name, computer) { + var key = '_' + name; + obj.prototype[name] = function cachedProperty() { + return this[key] !== undefined ? this[key] : + this[key] = computer.call(this); + }; +} +utils.cachedProperty = cachedProperty; + +function parseBytes(bytes) { + return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') : + bytes; +} +utils.parseBytes = parseBytes; + +function intFromLE(bytes) { + return new BN(bytes, 'hex', 'le'); +} +utils.intFromLE = intFromLE; + + +},{"bn.js":124,"minimalistic-assert":163,"minimalistic-crypto-utils":164}],124:[function(require,module,exports){ +(function (module, exports) { + 'use strict'; + + // Utils + function assert (val, msg) { + if (!val) throw new Error(msg || 'Assertion failed'); + } + + // Could use `inherits` module, but don't want to move from single file + // architecture yet. + function inherits (ctor, superCtor) { + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; + } + + // BN + + function BN (number, base, endian) { + if (BN.isBN(number)) { + return number; + } + + this.negative = 0; + this.words = null; + this.length = 0; + + // Reduction context + this.red = null; + + if (number !== null) { + if (base === 'le' || base === 'be') { + endian = base; + base = 10; + } + + this._init(number || 0, base || 10, endian || 'be'); + } + } + if (typeof module === 'object') { + module.exports = BN; + } else { + exports.BN = BN; + } + + BN.BN = BN; + BN.wordSize = 26; + + var Buffer; + try { + if (typeof window !== 'undefined' && typeof window.Buffer !== 'undefined') { + Buffer = window.Buffer; + } else { + Buffer = require('buffer').Buffer; + } + } catch (e) { + } + + BN.isBN = function isBN (num) { + if (num instanceof BN) { + return true; + } + + return num !== null && typeof num === 'object' && + num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); + }; + + BN.max = function max (left, right) { + if (left.cmp(right) > 0) return left; + return right; + }; + + BN.min = function min (left, right) { + if (left.cmp(right) < 0) return left; + return right; + }; + + BN.prototype._init = function init (number, base, endian) { + if (typeof number === 'number') { + return this._initNumber(number, base, endian); + } + + if (typeof number === 'object') { + return this._initArray(number, base, endian); + } + + if (base === 'hex') { + base = 16; + } + assert(base === (base | 0) && base >= 2 && base <= 36); + + number = number.toString().replace(/\s+/g, ''); + var start = 0; + if (number[0] === '-') { + start++; + this.negative = 1; + } + + if (start < number.length) { + if (base === 16) { + this._parseHex(number, start, endian); + } else { + this._parseBase(number, base, start); + if (endian === 'le') { + this._initArray(this.toArray(), base, endian); + } + } + } + }; + + BN.prototype._initNumber = function _initNumber (number, base, endian) { + if (number < 0) { + this.negative = 1; + number = -number; + } + if (number < 0x4000000) { + this.words = [ number & 0x3ffffff ]; + this.length = 1; + } else if (number < 0x10000000000000) { + this.words = [ + number & 0x3ffffff, + (number / 0x4000000) & 0x3ffffff + ]; + this.length = 2; + } else { + assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) + this.words = [ + number & 0x3ffffff, + (number / 0x4000000) & 0x3ffffff, + 1 + ]; + this.length = 3; + } + + if (endian !== 'le') return; + + // Reverse the bytes + this._initArray(this.toArray(), base, endian); + }; + + BN.prototype._initArray = function _initArray (number, base, endian) { + // Perhaps a Uint8Array + assert(typeof number.length === 'number'); + if (number.length <= 0) { + this.words = [ 0 ]; + this.length = 1; + return this; + } + + this.length = Math.ceil(number.length / 3); + this.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + this.words[i] = 0; + } + + var j, w; + var off = 0; + if (endian === 'be') { + for (i = number.length - 1, j = 0; i >= 0; i -= 3) { + w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + } else if (endian === 'le') { + for (i = 0, j = 0; i < number.length; i += 3) { + w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + } + return this.strip(); + }; + + function parseHex4Bits (string, index) { + var c = string.charCodeAt(index); + // 'A' - 'F' + if (c >= 65 && c <= 70) { + return c - 55; + // 'a' - 'f' + } else if (c >= 97 && c <= 102) { + return c - 87; + // '0' - '9' + } else { + return (c - 48) & 0xf; + } + } + + function parseHexByte (string, lowerBound, index) { + var r = parseHex4Bits(string, index); + if (index - 1 >= lowerBound) { + r |= parseHex4Bits(string, index - 1) << 4; + } + return r; + } + + BN.prototype._parseHex = function _parseHex (number, start, endian) { + // Create possibly bigger array to ensure that it fits the number + this.length = Math.ceil((number.length - start) / 6); + this.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + this.words[i] = 0; + } + + // 24-bits chunks + var off = 0; + var j = 0; + + var w; + if (endian === 'be') { + for (i = number.length - 1; i >= start; i -= 2) { + w = parseHexByte(number, start, i) << off; + this.words[j] |= w & 0x3ffffff; + if (off >= 18) { + off -= 18; + j += 1; + this.words[j] |= w >>> 26; + } else { + off += 8; + } + } + } else { + var parseLength = number.length - start; + for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { + w = parseHexByte(number, start, i) << off; + this.words[j] |= w & 0x3ffffff; + if (off >= 18) { + off -= 18; + j += 1; + this.words[j] |= w >>> 26; + } else { + off += 8; + } + } + } + + this.strip(); + }; + + function parseBase (str, start, end, mul) { + var r = 0; + var len = Math.min(str.length, end); + for (var i = start; i < len; i++) { + var c = str.charCodeAt(i) - 48; + + r *= mul; + + // 'a' + if (c >= 49) { + r += c - 49 + 0xa; + + // 'A' + } else if (c >= 17) { + r += c - 17 + 0xa; + + // '0' - '9' + } else { + r += c; + } + } + return r; + } + + BN.prototype._parseBase = function _parseBase (number, base, start) { + // Initialize as zero + this.words = [ 0 ]; + this.length = 1; + + // Find length of limb in base + for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { + limbLen++; + } + limbLen--; + limbPow = (limbPow / base) | 0; + + var total = number.length - start; + var mod = total % limbLen; + var end = Math.min(total, total - mod) + start; + + var word = 0; + for (var i = start; i < end; i += limbLen) { + word = parseBase(number, i, i + limbLen, base); + + this.imuln(limbPow); + if (this.words[0] + word < 0x4000000) { + this.words[0] += word; + } else { + this._iaddn(word); + } + } + + if (mod !== 0) { + var pow = 1; + word = parseBase(number, i, number.length, base); + + for (i = 0; i < mod; i++) { + pow *= base; + } + + this.imuln(pow); + if (this.words[0] + word < 0x4000000) { + this.words[0] += word; + } else { + this._iaddn(word); + } + } + + this.strip(); + }; + + BN.prototype.copy = function copy (dest) { + dest.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + dest.words[i] = this.words[i]; + } + dest.length = this.length; + dest.negative = this.negative; + dest.red = this.red; + }; + + BN.prototype.clone = function clone () { + var r = new BN(null); + this.copy(r); + return r; + }; + + BN.prototype._expand = function _expand (size) { + while (this.length < size) { + this.words[this.length++] = 0; + } + return this; + }; + + // Remove leading `0` from `this` + BN.prototype.strip = function strip () { + while (this.length > 1 && this.words[this.length - 1] === 0) { + this.length--; + } + return this._normSign(); + }; + + BN.prototype._normSign = function _normSign () { + // -0 = 0 + if (this.length === 1 && this.words[0] === 0) { + this.negative = 0; + } + return this; + }; + + BN.prototype.inspect = function inspect () { + return (this.red ? ''; + }; + + /* + + var zeros = []; + var groupSizes = []; + var groupBases = []; + + var s = ''; + var i = -1; + while (++i < BN.wordSize) { + zeros[i] = s; + s += '0'; + } + groupSizes[0] = 0; + groupSizes[1] = 0; + groupBases[0] = 0; + groupBases[1] = 0; + var base = 2 - 1; + while (++base < 36 + 1) { + var groupSize = 0; + var groupBase = 1; + while (groupBase < (1 << BN.wordSize) / base) { + groupBase *= base; + groupSize += 1; + } + groupSizes[base] = groupSize; + groupBases[base] = groupBase; + } + + */ + + var zeros = [ + '', + '0', + '00', + '000', + '0000', + '00000', + '000000', + '0000000', + '00000000', + '000000000', + '0000000000', + '00000000000', + '000000000000', + '0000000000000', + '00000000000000', + '000000000000000', + '0000000000000000', + '00000000000000000', + '000000000000000000', + '0000000000000000000', + '00000000000000000000', + '000000000000000000000', + '0000000000000000000000', + '00000000000000000000000', + '000000000000000000000000', + '0000000000000000000000000' + ]; + + var groupSizes = [ + 0, 0, + 25, 16, 12, 11, 10, 9, 8, + 8, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 5, 5, + 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5 + ]; + + var groupBases = [ + 0, 0, + 33554432, 43046721, 16777216, 48828125, 60466176, 40353607, 16777216, + 43046721, 10000000, 19487171, 35831808, 62748517, 7529536, 11390625, + 16777216, 24137569, 34012224, 47045881, 64000000, 4084101, 5153632, + 6436343, 7962624, 9765625, 11881376, 14348907, 17210368, 20511149, + 24300000, 28629151, 33554432, 39135393, 45435424, 52521875, 60466176 + ]; + + BN.prototype.toString = function toString (base, padding) { + base = base || 10; + padding = padding | 0 || 1; + + var out; + if (base === 16 || base === 'hex') { + out = ''; + var off = 0; + var carry = 0; + for (var i = 0; i < this.length; i++) { + var w = this.words[i]; + var word = (((w << off) | carry) & 0xffffff).toString(16); + carry = (w >>> (24 - off)) & 0xffffff; + if (carry !== 0 || i !== this.length - 1) { + out = zeros[6 - word.length] + word + out; + } else { + out = word + out; + } + off += 2; + if (off >= 26) { + off -= 26; + i--; + } + } + if (carry !== 0) { + out = carry.toString(16) + out; + } + while (out.length % padding !== 0) { + out = '0' + out; + } + if (this.negative !== 0) { + out = '-' + out; + } + return out; + } + + if (base === (base | 0) && base >= 2 && base <= 36) { + // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); + var groupSize = groupSizes[base]; + // var groupBase = Math.pow(base, groupSize); + var groupBase = groupBases[base]; + out = ''; + var c = this.clone(); + c.negative = 0; + while (!c.isZero()) { + var r = c.modn(groupBase).toString(base); + c = c.idivn(groupBase); + + if (!c.isZero()) { + out = zeros[groupSize - r.length] + r + out; + } else { + out = r + out; + } + } + if (this.isZero()) { + out = '0' + out; + } + while (out.length % padding !== 0) { + out = '0' + out; + } + if (this.negative !== 0) { + out = '-' + out; + } + return out; + } + + assert(false, 'Base should be between 2 and 36'); + }; + + BN.prototype.toNumber = function toNumber () { + var ret = this.words[0]; + if (this.length === 2) { + ret += this.words[1] * 0x4000000; + } else if (this.length === 3 && this.words[2] === 0x01) { + // NOTE: at this stage it is known that the top bit is set + ret += 0x10000000000000 + (this.words[1] * 0x4000000); + } else if (this.length > 2) { + assert(false, 'Number can only safely store up to 53 bits'); + } + return (this.negative !== 0) ? -ret : ret; + }; + + BN.prototype.toJSON = function toJSON () { + return this.toString(16); + }; + + BN.prototype.toBuffer = function toBuffer (endian, length) { + assert(typeof Buffer !== 'undefined'); + return this.toArrayLike(Buffer, endian, length); + }; + + BN.prototype.toArray = function toArray (endian, length) { + return this.toArrayLike(Array, endian, length); + }; + + BN.prototype.toArrayLike = function toArrayLike (ArrayType, endian, length) { + var byteLength = this.byteLength(); + var reqLength = length || Math.max(1, byteLength); + assert(byteLength <= reqLength, 'byte array longer than desired length'); + assert(reqLength > 0, 'Requested array length <= 0'); + + this.strip(); + var littleEndian = endian === 'le'; + var res = new ArrayType(reqLength); + + var b, i; + var q = this.clone(); + if (!littleEndian) { + // Assume big-endian + for (i = 0; i < reqLength - byteLength; i++) { + res[i] = 0; + } + + for (i = 0; !q.isZero(); i++) { + b = q.andln(0xff); + q.iushrn(8); + + res[reqLength - i - 1] = b; + } + } else { + for (i = 0; !q.isZero(); i++) { + b = q.andln(0xff); + q.iushrn(8); + + res[i] = b; + } + + for (; i < reqLength; i++) { + res[i] = 0; + } + } + + return res; + }; + + if (Math.clz32) { + BN.prototype._countBits = function _countBits (w) { + return 32 - Math.clz32(w); + }; + } else { + BN.prototype._countBits = function _countBits (w) { + var t = w; + var r = 0; + if (t >= 0x1000) { + r += 13; + t >>>= 13; + } + if (t >= 0x40) { + r += 7; + t >>>= 7; + } + if (t >= 0x8) { + r += 4; + t >>>= 4; + } + if (t >= 0x02) { + r += 2; + t >>>= 2; + } + return r + t; + }; + } + + BN.prototype._zeroBits = function _zeroBits (w) { + // Short-cut + if (w === 0) return 26; + + var t = w; + var r = 0; + if ((t & 0x1fff) === 0) { + r += 13; + t >>>= 13; + } + if ((t & 0x7f) === 0) { + r += 7; + t >>>= 7; + } + if ((t & 0xf) === 0) { + r += 4; + t >>>= 4; + } + if ((t & 0x3) === 0) { + r += 2; + t >>>= 2; + } + if ((t & 0x1) === 0) { + r++; + } + return r; + }; + + // Return number of used bits in a BN + BN.prototype.bitLength = function bitLength () { + var w = this.words[this.length - 1]; + var hi = this._countBits(w); + return (this.length - 1) * 26 + hi; + }; + + function toBitArray (num) { + var w = new Array(num.bitLength()); + + for (var bit = 0; bit < w.length; bit++) { + var off = (bit / 26) | 0; + var wbit = bit % 26; + + w[bit] = (num.words[off] & (1 << wbit)) >>> wbit; + } + + return w; + } + + // Number of trailing zero bits + BN.prototype.zeroBits = function zeroBits () { + if (this.isZero()) return 0; + + var r = 0; + for (var i = 0; i < this.length; i++) { + var b = this._zeroBits(this.words[i]); + r += b; + if (b !== 26) break; + } + return r; + }; + + BN.prototype.byteLength = function byteLength () { + return Math.ceil(this.bitLength() / 8); + }; + + BN.prototype.toTwos = function toTwos (width) { + if (this.negative !== 0) { + return this.abs().inotn(width).iaddn(1); + } + return this.clone(); + }; + + BN.prototype.fromTwos = function fromTwos (width) { + if (this.testn(width - 1)) { + return this.notn(width).iaddn(1).ineg(); + } + return this.clone(); + }; + + BN.prototype.isNeg = function isNeg () { + return this.negative !== 0; + }; + + // Return negative clone of `this` + BN.prototype.neg = function neg () { + return this.clone().ineg(); + }; + + BN.prototype.ineg = function ineg () { + if (!this.isZero()) { + this.negative ^= 1; + } + + return this; + }; + + // Or `num` with `this` in-place + BN.prototype.iuor = function iuor (num) { + while (this.length < num.length) { + this.words[this.length++] = 0; + } + + for (var i = 0; i < num.length; i++) { + this.words[i] = this.words[i] | num.words[i]; + } + + return this.strip(); + }; + + BN.prototype.ior = function ior (num) { + assert((this.negative | num.negative) === 0); + return this.iuor(num); + }; + + // Or `num` with `this` + BN.prototype.or = function or (num) { + if (this.length > num.length) return this.clone().ior(num); + return num.clone().ior(this); + }; + + BN.prototype.uor = function uor (num) { + if (this.length > num.length) return this.clone().iuor(num); + return num.clone().iuor(this); + }; + + // And `num` with `this` in-place + BN.prototype.iuand = function iuand (num) { + // b = min-length(num, this) + var b; + if (this.length > num.length) { + b = num; + } else { + b = this; + } + + for (var i = 0; i < b.length; i++) { + this.words[i] = this.words[i] & num.words[i]; + } + + this.length = b.length; + + return this.strip(); + }; + + BN.prototype.iand = function iand (num) { + assert((this.negative | num.negative) === 0); + return this.iuand(num); + }; + + // And `num` with `this` + BN.prototype.and = function and (num) { + if (this.length > num.length) return this.clone().iand(num); + return num.clone().iand(this); + }; + + BN.prototype.uand = function uand (num) { + if (this.length > num.length) return this.clone().iuand(num); + return num.clone().iuand(this); + }; + + // Xor `num` with `this` in-place + BN.prototype.iuxor = function iuxor (num) { + // a.length > b.length + var a; + var b; + if (this.length > num.length) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + for (var i = 0; i < b.length; i++) { + this.words[i] = a.words[i] ^ b.words[i]; + } + + if (this !== a) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + this.length = a.length; + + return this.strip(); + }; + + BN.prototype.ixor = function ixor (num) { + assert((this.negative | num.negative) === 0); + return this.iuxor(num); + }; + + // Xor `num` with `this` + BN.prototype.xor = function xor (num) { + if (this.length > num.length) return this.clone().ixor(num); + return num.clone().ixor(this); + }; + + BN.prototype.uxor = function uxor (num) { + if (this.length > num.length) return this.clone().iuxor(num); + return num.clone().iuxor(this); + }; + + // Not ``this`` with ``width`` bitwidth + BN.prototype.inotn = function inotn (width) { + assert(typeof width === 'number' && width >= 0); + + var bytesNeeded = Math.ceil(width / 26) | 0; + var bitsLeft = width % 26; + + // Extend the buffer with leading zeroes + this._expand(bytesNeeded); + + if (bitsLeft > 0) { + bytesNeeded--; + } + + // Handle complete words + for (var i = 0; i < bytesNeeded; i++) { + this.words[i] = ~this.words[i] & 0x3ffffff; + } + + // Handle the residue + if (bitsLeft > 0) { + this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); + } + + // And remove leading zeroes + return this.strip(); + }; + + BN.prototype.notn = function notn (width) { + return this.clone().inotn(width); + }; + + // Set `bit` of `this` + BN.prototype.setn = function setn (bit, val) { + assert(typeof bit === 'number' && bit >= 0); + + var off = (bit / 26) | 0; + var wbit = bit % 26; + + this._expand(off + 1); + + if (val) { + this.words[off] = this.words[off] | (1 << wbit); + } else { + this.words[off] = this.words[off] & ~(1 << wbit); + } + + return this.strip(); + }; + + // Add `num` to `this` in-place + BN.prototype.iadd = function iadd (num) { + var r; + + // negative + positive + if (this.negative !== 0 && num.negative === 0) { + this.negative = 0; + r = this.isub(num); + this.negative ^= 1; + return this._normSign(); + + // positive + negative + } else if (this.negative === 0 && num.negative !== 0) { + num.negative = 0; + r = this.isub(num); + num.negative = 1; + return r._normSign(); + } + + // a.length > b.length + var a, b; + if (this.length > num.length) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + var carry = 0; + for (var i = 0; i < b.length; i++) { + r = (a.words[i] | 0) + (b.words[i] | 0) + carry; + this.words[i] = r & 0x3ffffff; + carry = r >>> 26; + } + for (; carry !== 0 && i < a.length; i++) { + r = (a.words[i] | 0) + carry; + this.words[i] = r & 0x3ffffff; + carry = r >>> 26; + } + + this.length = a.length; + if (carry !== 0) { + this.words[this.length] = carry; + this.length++; + // Copy the rest of the words + } else if (a !== this) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + return this; + }; + + // Add `num` to `this` + BN.prototype.add = function add (num) { + var res; + if (num.negative !== 0 && this.negative === 0) { + num.negative = 0; + res = this.sub(num); + num.negative ^= 1; + return res; + } else if (num.negative === 0 && this.negative !== 0) { + this.negative = 0; + res = num.sub(this); + this.negative = 1; + return res; + } + + if (this.length > num.length) return this.clone().iadd(num); + + return num.clone().iadd(this); + }; + + // Subtract `num` from `this` in-place + BN.prototype.isub = function isub (num) { + // this - (-num) = this + num + if (num.negative !== 0) { + num.negative = 0; + var r = this.iadd(num); + num.negative = 1; + return r._normSign(); + + // -this - num = -(this + num) + } else if (this.negative !== 0) { + this.negative = 0; + this.iadd(num); + this.negative = 1; + return this._normSign(); + } + + // At this point both numbers are positive + var cmp = this.cmp(num); + + // Optimization - zeroify + if (cmp === 0) { + this.negative = 0; + this.length = 1; + this.words[0] = 0; + return this; + } + + // a > b + var a, b; + if (cmp > 0) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + var carry = 0; + for (var i = 0; i < b.length; i++) { + r = (a.words[i] | 0) - (b.words[i] | 0) + carry; + carry = r >> 26; + this.words[i] = r & 0x3ffffff; + } + for (; carry !== 0 && i < a.length; i++) { + r = (a.words[i] | 0) + carry; + carry = r >> 26; + this.words[i] = r & 0x3ffffff; + } + + // Copy rest of the words + if (carry === 0 && i < a.length && a !== this) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + this.length = Math.max(this.length, i); + + if (a !== this) { + this.negative = 1; + } + + return this.strip(); + }; + + // Subtract `num` from `this` + BN.prototype.sub = function sub (num) { + return this.clone().isub(num); + }; + + function smallMulTo (self, num, out) { + out.negative = num.negative ^ self.negative; + var len = (self.length + num.length) | 0; + out.length = len; + len = (len - 1) | 0; + + // Peel one iteration (compiler can't do it, because of code complexity) + var a = self.words[0] | 0; + var b = num.words[0] | 0; + var r = a * b; + + var lo = r & 0x3ffffff; + var carry = (r / 0x4000000) | 0; + out.words[0] = lo; + + for (var k = 1; k < len; k++) { + // Sum all words with the same `i + j = k` and accumulate `ncarry`, + // note that ncarry could be >= 0x3ffffff + var ncarry = carry >>> 26; + var rword = carry & 0x3ffffff; + var maxJ = Math.min(k, num.length - 1); + for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { + var i = (k - j) | 0; + a = self.words[i] | 0; + b = num.words[j] | 0; + r = a * b + rword; + ncarry += (r / 0x4000000) | 0; + rword = r & 0x3ffffff; + } + out.words[k] = rword | 0; + carry = ncarry | 0; + } + if (carry !== 0) { + out.words[k] = carry | 0; + } else { + out.length--; + } + + return out.strip(); + } + + // TODO(indutny): it may be reasonable to omit it for users who don't need + // to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit + // multiplication (like elliptic secp256k1). + var comb10MulTo = function comb10MulTo (self, num, out) { + var a = self.words; + var b = num.words; + var o = out.words; + var c = 0; + var lo; + var mid; + var hi; + var a0 = a[0] | 0; + var al0 = a0 & 0x1fff; + var ah0 = a0 >>> 13; + var a1 = a[1] | 0; + var al1 = a1 & 0x1fff; + var ah1 = a1 >>> 13; + var a2 = a[2] | 0; + var al2 = a2 & 0x1fff; + var ah2 = a2 >>> 13; + var a3 = a[3] | 0; + var al3 = a3 & 0x1fff; + var ah3 = a3 >>> 13; + var a4 = a[4] | 0; + var al4 = a4 & 0x1fff; + var ah4 = a4 >>> 13; + var a5 = a[5] | 0; + var al5 = a5 & 0x1fff; + var ah5 = a5 >>> 13; + var a6 = a[6] | 0; + var al6 = a6 & 0x1fff; + var ah6 = a6 >>> 13; + var a7 = a[7] | 0; + var al7 = a7 & 0x1fff; + var ah7 = a7 >>> 13; + var a8 = a[8] | 0; + var al8 = a8 & 0x1fff; + var ah8 = a8 >>> 13; + var a9 = a[9] | 0; + var al9 = a9 & 0x1fff; + var ah9 = a9 >>> 13; + var b0 = b[0] | 0; + var bl0 = b0 & 0x1fff; + var bh0 = b0 >>> 13; + var b1 = b[1] | 0; + var bl1 = b1 & 0x1fff; + var bh1 = b1 >>> 13; + var b2 = b[2] | 0; + var bl2 = b2 & 0x1fff; + var bh2 = b2 >>> 13; + var b3 = b[3] | 0; + var bl3 = b3 & 0x1fff; + var bh3 = b3 >>> 13; + var b4 = b[4] | 0; + var bl4 = b4 & 0x1fff; + var bh4 = b4 >>> 13; + var b5 = b[5] | 0; + var bl5 = b5 & 0x1fff; + var bh5 = b5 >>> 13; + var b6 = b[6] | 0; + var bl6 = b6 & 0x1fff; + var bh6 = b6 >>> 13; + var b7 = b[7] | 0; + var bl7 = b7 & 0x1fff; + var bh7 = b7 >>> 13; + var b8 = b[8] | 0; + var bl8 = b8 & 0x1fff; + var bh8 = b8 >>> 13; + var b9 = b[9] | 0; + var bl9 = b9 & 0x1fff; + var bh9 = b9 >>> 13; + + out.negative = self.negative ^ num.negative; + out.length = 19; + /* k = 0 */ + lo = Math.imul(al0, bl0); + mid = Math.imul(al0, bh0); + mid = (mid + Math.imul(ah0, bl0)) | 0; + hi = Math.imul(ah0, bh0); + var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; + w0 &= 0x3ffffff; + /* k = 1 */ + lo = Math.imul(al1, bl0); + mid = Math.imul(al1, bh0); + mid = (mid + Math.imul(ah1, bl0)) | 0; + hi = Math.imul(ah1, bh0); + lo = (lo + Math.imul(al0, bl1)) | 0; + mid = (mid + Math.imul(al0, bh1)) | 0; + mid = (mid + Math.imul(ah0, bl1)) | 0; + hi = (hi + Math.imul(ah0, bh1)) | 0; + var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; + w1 &= 0x3ffffff; + /* k = 2 */ + lo = Math.imul(al2, bl0); + mid = Math.imul(al2, bh0); + mid = (mid + Math.imul(ah2, bl0)) | 0; + hi = Math.imul(ah2, bh0); + lo = (lo + Math.imul(al1, bl1)) | 0; + mid = (mid + Math.imul(al1, bh1)) | 0; + mid = (mid + Math.imul(ah1, bl1)) | 0; + hi = (hi + Math.imul(ah1, bh1)) | 0; + lo = (lo + Math.imul(al0, bl2)) | 0; + mid = (mid + Math.imul(al0, bh2)) | 0; + mid = (mid + Math.imul(ah0, bl2)) | 0; + hi = (hi + Math.imul(ah0, bh2)) | 0; + var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; + w2 &= 0x3ffffff; + /* k = 3 */ + lo = Math.imul(al3, bl0); + mid = Math.imul(al3, bh0); + mid = (mid + Math.imul(ah3, bl0)) | 0; + hi = Math.imul(ah3, bh0); + lo = (lo + Math.imul(al2, bl1)) | 0; + mid = (mid + Math.imul(al2, bh1)) | 0; + mid = (mid + Math.imul(ah2, bl1)) | 0; + hi = (hi + Math.imul(ah2, bh1)) | 0; + lo = (lo + Math.imul(al1, bl2)) | 0; + mid = (mid + Math.imul(al1, bh2)) | 0; + mid = (mid + Math.imul(ah1, bl2)) | 0; + hi = (hi + Math.imul(ah1, bh2)) | 0; + lo = (lo + Math.imul(al0, bl3)) | 0; + mid = (mid + Math.imul(al0, bh3)) | 0; + mid = (mid + Math.imul(ah0, bl3)) | 0; + hi = (hi + Math.imul(ah0, bh3)) | 0; + var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; + w3 &= 0x3ffffff; + /* k = 4 */ + lo = Math.imul(al4, bl0); + mid = Math.imul(al4, bh0); + mid = (mid + Math.imul(ah4, bl0)) | 0; + hi = Math.imul(ah4, bh0); + lo = (lo + Math.imul(al3, bl1)) | 0; + mid = (mid + Math.imul(al3, bh1)) | 0; + mid = (mid + Math.imul(ah3, bl1)) | 0; + hi = (hi + Math.imul(ah3, bh1)) | 0; + lo = (lo + Math.imul(al2, bl2)) | 0; + mid = (mid + Math.imul(al2, bh2)) | 0; + mid = (mid + Math.imul(ah2, bl2)) | 0; + hi = (hi + Math.imul(ah2, bh2)) | 0; + lo = (lo + Math.imul(al1, bl3)) | 0; + mid = (mid + Math.imul(al1, bh3)) | 0; + mid = (mid + Math.imul(ah1, bl3)) | 0; + hi = (hi + Math.imul(ah1, bh3)) | 0; + lo = (lo + Math.imul(al0, bl4)) | 0; + mid = (mid + Math.imul(al0, bh4)) | 0; + mid = (mid + Math.imul(ah0, bl4)) | 0; + hi = (hi + Math.imul(ah0, bh4)) | 0; + var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; + w4 &= 0x3ffffff; + /* k = 5 */ + lo = Math.imul(al5, bl0); + mid = Math.imul(al5, bh0); + mid = (mid + Math.imul(ah5, bl0)) | 0; + hi = Math.imul(ah5, bh0); + lo = (lo + Math.imul(al4, bl1)) | 0; + mid = (mid + Math.imul(al4, bh1)) | 0; + mid = (mid + Math.imul(ah4, bl1)) | 0; + hi = (hi + Math.imul(ah4, bh1)) | 0; + lo = (lo + Math.imul(al3, bl2)) | 0; + mid = (mid + Math.imul(al3, bh2)) | 0; + mid = (mid + Math.imul(ah3, bl2)) | 0; + hi = (hi + Math.imul(ah3, bh2)) | 0; + lo = (lo + Math.imul(al2, bl3)) | 0; + mid = (mid + Math.imul(al2, bh3)) | 0; + mid = (mid + Math.imul(ah2, bl3)) | 0; + hi = (hi + Math.imul(ah2, bh3)) | 0; + lo = (lo + Math.imul(al1, bl4)) | 0; + mid = (mid + Math.imul(al1, bh4)) | 0; + mid = (mid + Math.imul(ah1, bl4)) | 0; + hi = (hi + Math.imul(ah1, bh4)) | 0; + lo = (lo + Math.imul(al0, bl5)) | 0; + mid = (mid + Math.imul(al0, bh5)) | 0; + mid = (mid + Math.imul(ah0, bl5)) | 0; + hi = (hi + Math.imul(ah0, bh5)) | 0; + var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; + w5 &= 0x3ffffff; + /* k = 6 */ + lo = Math.imul(al6, bl0); + mid = Math.imul(al6, bh0); + mid = (mid + Math.imul(ah6, bl0)) | 0; + hi = Math.imul(ah6, bh0); + lo = (lo + Math.imul(al5, bl1)) | 0; + mid = (mid + Math.imul(al5, bh1)) | 0; + mid = (mid + Math.imul(ah5, bl1)) | 0; + hi = (hi + Math.imul(ah5, bh1)) | 0; + lo = (lo + Math.imul(al4, bl2)) | 0; + mid = (mid + Math.imul(al4, bh2)) | 0; + mid = (mid + Math.imul(ah4, bl2)) | 0; + hi = (hi + Math.imul(ah4, bh2)) | 0; + lo = (lo + Math.imul(al3, bl3)) | 0; + mid = (mid + Math.imul(al3, bh3)) | 0; + mid = (mid + Math.imul(ah3, bl3)) | 0; + hi = (hi + Math.imul(ah3, bh3)) | 0; + lo = (lo + Math.imul(al2, bl4)) | 0; + mid = (mid + Math.imul(al2, bh4)) | 0; + mid = (mid + Math.imul(ah2, bl4)) | 0; + hi = (hi + Math.imul(ah2, bh4)) | 0; + lo = (lo + Math.imul(al1, bl5)) | 0; + mid = (mid + Math.imul(al1, bh5)) | 0; + mid = (mid + Math.imul(ah1, bl5)) | 0; + hi = (hi + Math.imul(ah1, bh5)) | 0; + lo = (lo + Math.imul(al0, bl6)) | 0; + mid = (mid + Math.imul(al0, bh6)) | 0; + mid = (mid + Math.imul(ah0, bl6)) | 0; + hi = (hi + Math.imul(ah0, bh6)) | 0; + var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; + w6 &= 0x3ffffff; + /* k = 7 */ + lo = Math.imul(al7, bl0); + mid = Math.imul(al7, bh0); + mid = (mid + Math.imul(ah7, bl0)) | 0; + hi = Math.imul(ah7, bh0); + lo = (lo + Math.imul(al6, bl1)) | 0; + mid = (mid + Math.imul(al6, bh1)) | 0; + mid = (mid + Math.imul(ah6, bl1)) | 0; + hi = (hi + Math.imul(ah6, bh1)) | 0; + lo = (lo + Math.imul(al5, bl2)) | 0; + mid = (mid + Math.imul(al5, bh2)) | 0; + mid = (mid + Math.imul(ah5, bl2)) | 0; + hi = (hi + Math.imul(ah5, bh2)) | 0; + lo = (lo + Math.imul(al4, bl3)) | 0; + mid = (mid + Math.imul(al4, bh3)) | 0; + mid = (mid + Math.imul(ah4, bl3)) | 0; + hi = (hi + Math.imul(ah4, bh3)) | 0; + lo = (lo + Math.imul(al3, bl4)) | 0; + mid = (mid + Math.imul(al3, bh4)) | 0; + mid = (mid + Math.imul(ah3, bl4)) | 0; + hi = (hi + Math.imul(ah3, bh4)) | 0; + lo = (lo + Math.imul(al2, bl5)) | 0; + mid = (mid + Math.imul(al2, bh5)) | 0; + mid = (mid + Math.imul(ah2, bl5)) | 0; + hi = (hi + Math.imul(ah2, bh5)) | 0; + lo = (lo + Math.imul(al1, bl6)) | 0; + mid = (mid + Math.imul(al1, bh6)) | 0; + mid = (mid + Math.imul(ah1, bl6)) | 0; + hi = (hi + Math.imul(ah1, bh6)) | 0; + lo = (lo + Math.imul(al0, bl7)) | 0; + mid = (mid + Math.imul(al0, bh7)) | 0; + mid = (mid + Math.imul(ah0, bl7)) | 0; + hi = (hi + Math.imul(ah0, bh7)) | 0; + var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; + w7 &= 0x3ffffff; + /* k = 8 */ + lo = Math.imul(al8, bl0); + mid = Math.imul(al8, bh0); + mid = (mid + Math.imul(ah8, bl0)) | 0; + hi = Math.imul(ah8, bh0); + lo = (lo + Math.imul(al7, bl1)) | 0; + mid = (mid + Math.imul(al7, bh1)) | 0; + mid = (mid + Math.imul(ah7, bl1)) | 0; + hi = (hi + Math.imul(ah7, bh1)) | 0; + lo = (lo + Math.imul(al6, bl2)) | 0; + mid = (mid + Math.imul(al6, bh2)) | 0; + mid = (mid + Math.imul(ah6, bl2)) | 0; + hi = (hi + Math.imul(ah6, bh2)) | 0; + lo = (lo + Math.imul(al5, bl3)) | 0; + mid = (mid + Math.imul(al5, bh3)) | 0; + mid = (mid + Math.imul(ah5, bl3)) | 0; + hi = (hi + Math.imul(ah5, bh3)) | 0; + lo = (lo + Math.imul(al4, bl4)) | 0; + mid = (mid + Math.imul(al4, bh4)) | 0; + mid = (mid + Math.imul(ah4, bl4)) | 0; + hi = (hi + Math.imul(ah4, bh4)) | 0; + lo = (lo + Math.imul(al3, bl5)) | 0; + mid = (mid + Math.imul(al3, bh5)) | 0; + mid = (mid + Math.imul(ah3, bl5)) | 0; + hi = (hi + Math.imul(ah3, bh5)) | 0; + lo = (lo + Math.imul(al2, bl6)) | 0; + mid = (mid + Math.imul(al2, bh6)) | 0; + mid = (mid + Math.imul(ah2, bl6)) | 0; + hi = (hi + Math.imul(ah2, bh6)) | 0; + lo = (lo + Math.imul(al1, bl7)) | 0; + mid = (mid + Math.imul(al1, bh7)) | 0; + mid = (mid + Math.imul(ah1, bl7)) | 0; + hi = (hi + Math.imul(ah1, bh7)) | 0; + lo = (lo + Math.imul(al0, bl8)) | 0; + mid = (mid + Math.imul(al0, bh8)) | 0; + mid = (mid + Math.imul(ah0, bl8)) | 0; + hi = (hi + Math.imul(ah0, bh8)) | 0; + var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; + w8 &= 0x3ffffff; + /* k = 9 */ + lo = Math.imul(al9, bl0); + mid = Math.imul(al9, bh0); + mid = (mid + Math.imul(ah9, bl0)) | 0; + hi = Math.imul(ah9, bh0); + lo = (lo + Math.imul(al8, bl1)) | 0; + mid = (mid + Math.imul(al8, bh1)) | 0; + mid = (mid + Math.imul(ah8, bl1)) | 0; + hi = (hi + Math.imul(ah8, bh1)) | 0; + lo = (lo + Math.imul(al7, bl2)) | 0; + mid = (mid + Math.imul(al7, bh2)) | 0; + mid = (mid + Math.imul(ah7, bl2)) | 0; + hi = (hi + Math.imul(ah7, bh2)) | 0; + lo = (lo + Math.imul(al6, bl3)) | 0; + mid = (mid + Math.imul(al6, bh3)) | 0; + mid = (mid + Math.imul(ah6, bl3)) | 0; + hi = (hi + Math.imul(ah6, bh3)) | 0; + lo = (lo + Math.imul(al5, bl4)) | 0; + mid = (mid + Math.imul(al5, bh4)) | 0; + mid = (mid + Math.imul(ah5, bl4)) | 0; + hi = (hi + Math.imul(ah5, bh4)) | 0; + lo = (lo + Math.imul(al4, bl5)) | 0; + mid = (mid + Math.imul(al4, bh5)) | 0; + mid = (mid + Math.imul(ah4, bl5)) | 0; + hi = (hi + Math.imul(ah4, bh5)) | 0; + lo = (lo + Math.imul(al3, bl6)) | 0; + mid = (mid + Math.imul(al3, bh6)) | 0; + mid = (mid + Math.imul(ah3, bl6)) | 0; + hi = (hi + Math.imul(ah3, bh6)) | 0; + lo = (lo + Math.imul(al2, bl7)) | 0; + mid = (mid + Math.imul(al2, bh7)) | 0; + mid = (mid + Math.imul(ah2, bl7)) | 0; + hi = (hi + Math.imul(ah2, bh7)) | 0; + lo = (lo + Math.imul(al1, bl8)) | 0; + mid = (mid + Math.imul(al1, bh8)) | 0; + mid = (mid + Math.imul(ah1, bl8)) | 0; + hi = (hi + Math.imul(ah1, bh8)) | 0; + lo = (lo + Math.imul(al0, bl9)) | 0; + mid = (mid + Math.imul(al0, bh9)) | 0; + mid = (mid + Math.imul(ah0, bl9)) | 0; + hi = (hi + Math.imul(ah0, bh9)) | 0; + var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; + w9 &= 0x3ffffff; + /* k = 10 */ + lo = Math.imul(al9, bl1); + mid = Math.imul(al9, bh1); + mid = (mid + Math.imul(ah9, bl1)) | 0; + hi = Math.imul(ah9, bh1); + lo = (lo + Math.imul(al8, bl2)) | 0; + mid = (mid + Math.imul(al8, bh2)) | 0; + mid = (mid + Math.imul(ah8, bl2)) | 0; + hi = (hi + Math.imul(ah8, bh2)) | 0; + lo = (lo + Math.imul(al7, bl3)) | 0; + mid = (mid + Math.imul(al7, bh3)) | 0; + mid = (mid + Math.imul(ah7, bl3)) | 0; + hi = (hi + Math.imul(ah7, bh3)) | 0; + lo = (lo + Math.imul(al6, bl4)) | 0; + mid = (mid + Math.imul(al6, bh4)) | 0; + mid = (mid + Math.imul(ah6, bl4)) | 0; + hi = (hi + Math.imul(ah6, bh4)) | 0; + lo = (lo + Math.imul(al5, bl5)) | 0; + mid = (mid + Math.imul(al5, bh5)) | 0; + mid = (mid + Math.imul(ah5, bl5)) | 0; + hi = (hi + Math.imul(ah5, bh5)) | 0; + lo = (lo + Math.imul(al4, bl6)) | 0; + mid = (mid + Math.imul(al4, bh6)) | 0; + mid = (mid + Math.imul(ah4, bl6)) | 0; + hi = (hi + Math.imul(ah4, bh6)) | 0; + lo = (lo + Math.imul(al3, bl7)) | 0; + mid = (mid + Math.imul(al3, bh7)) | 0; + mid = (mid + Math.imul(ah3, bl7)) | 0; + hi = (hi + Math.imul(ah3, bh7)) | 0; + lo = (lo + Math.imul(al2, bl8)) | 0; + mid = (mid + Math.imul(al2, bh8)) | 0; + mid = (mid + Math.imul(ah2, bl8)) | 0; + hi = (hi + Math.imul(ah2, bh8)) | 0; + lo = (lo + Math.imul(al1, bl9)) | 0; + mid = (mid + Math.imul(al1, bh9)) | 0; + mid = (mid + Math.imul(ah1, bl9)) | 0; + hi = (hi + Math.imul(ah1, bh9)) | 0; + var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; + w10 &= 0x3ffffff; + /* k = 11 */ + lo = Math.imul(al9, bl2); + mid = Math.imul(al9, bh2); + mid = (mid + Math.imul(ah9, bl2)) | 0; + hi = Math.imul(ah9, bh2); + lo = (lo + Math.imul(al8, bl3)) | 0; + mid = (mid + Math.imul(al8, bh3)) | 0; + mid = (mid + Math.imul(ah8, bl3)) | 0; + hi = (hi + Math.imul(ah8, bh3)) | 0; + lo = (lo + Math.imul(al7, bl4)) | 0; + mid = (mid + Math.imul(al7, bh4)) | 0; + mid = (mid + Math.imul(ah7, bl4)) | 0; + hi = (hi + Math.imul(ah7, bh4)) | 0; + lo = (lo + Math.imul(al6, bl5)) | 0; + mid = (mid + Math.imul(al6, bh5)) | 0; + mid = (mid + Math.imul(ah6, bl5)) | 0; + hi = (hi + Math.imul(ah6, bh5)) | 0; + lo = (lo + Math.imul(al5, bl6)) | 0; + mid = (mid + Math.imul(al5, bh6)) | 0; + mid = (mid + Math.imul(ah5, bl6)) | 0; + hi = (hi + Math.imul(ah5, bh6)) | 0; + lo = (lo + Math.imul(al4, bl7)) | 0; + mid = (mid + Math.imul(al4, bh7)) | 0; + mid = (mid + Math.imul(ah4, bl7)) | 0; + hi = (hi + Math.imul(ah4, bh7)) | 0; + lo = (lo + Math.imul(al3, bl8)) | 0; + mid = (mid + Math.imul(al3, bh8)) | 0; + mid = (mid + Math.imul(ah3, bl8)) | 0; + hi = (hi + Math.imul(ah3, bh8)) | 0; + lo = (lo + Math.imul(al2, bl9)) | 0; + mid = (mid + Math.imul(al2, bh9)) | 0; + mid = (mid + Math.imul(ah2, bl9)) | 0; + hi = (hi + Math.imul(ah2, bh9)) | 0; + var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; + w11 &= 0x3ffffff; + /* k = 12 */ + lo = Math.imul(al9, bl3); + mid = Math.imul(al9, bh3); + mid = (mid + Math.imul(ah9, bl3)) | 0; + hi = Math.imul(ah9, bh3); + lo = (lo + Math.imul(al8, bl4)) | 0; + mid = (mid + Math.imul(al8, bh4)) | 0; + mid = (mid + Math.imul(ah8, bl4)) | 0; + hi = (hi + Math.imul(ah8, bh4)) | 0; + lo = (lo + Math.imul(al7, bl5)) | 0; + mid = (mid + Math.imul(al7, bh5)) | 0; + mid = (mid + Math.imul(ah7, bl5)) | 0; + hi = (hi + Math.imul(ah7, bh5)) | 0; + lo = (lo + Math.imul(al6, bl6)) | 0; + mid = (mid + Math.imul(al6, bh6)) | 0; + mid = (mid + Math.imul(ah6, bl6)) | 0; + hi = (hi + Math.imul(ah6, bh6)) | 0; + lo = (lo + Math.imul(al5, bl7)) | 0; + mid = (mid + Math.imul(al5, bh7)) | 0; + mid = (mid + Math.imul(ah5, bl7)) | 0; + hi = (hi + Math.imul(ah5, bh7)) | 0; + lo = (lo + Math.imul(al4, bl8)) | 0; + mid = (mid + Math.imul(al4, bh8)) | 0; + mid = (mid + Math.imul(ah4, bl8)) | 0; + hi = (hi + Math.imul(ah4, bh8)) | 0; + lo = (lo + Math.imul(al3, bl9)) | 0; + mid = (mid + Math.imul(al3, bh9)) | 0; + mid = (mid + Math.imul(ah3, bl9)) | 0; + hi = (hi + Math.imul(ah3, bh9)) | 0; + var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; + w12 &= 0x3ffffff; + /* k = 13 */ + lo = Math.imul(al9, bl4); + mid = Math.imul(al9, bh4); + mid = (mid + Math.imul(ah9, bl4)) | 0; + hi = Math.imul(ah9, bh4); + lo = (lo + Math.imul(al8, bl5)) | 0; + mid = (mid + Math.imul(al8, bh5)) | 0; + mid = (mid + Math.imul(ah8, bl5)) | 0; + hi = (hi + Math.imul(ah8, bh5)) | 0; + lo = (lo + Math.imul(al7, bl6)) | 0; + mid = (mid + Math.imul(al7, bh6)) | 0; + mid = (mid + Math.imul(ah7, bl6)) | 0; + hi = (hi + Math.imul(ah7, bh6)) | 0; + lo = (lo + Math.imul(al6, bl7)) | 0; + mid = (mid + Math.imul(al6, bh7)) | 0; + mid = (mid + Math.imul(ah6, bl7)) | 0; + hi = (hi + Math.imul(ah6, bh7)) | 0; + lo = (lo + Math.imul(al5, bl8)) | 0; + mid = (mid + Math.imul(al5, bh8)) | 0; + mid = (mid + Math.imul(ah5, bl8)) | 0; + hi = (hi + Math.imul(ah5, bh8)) | 0; + lo = (lo + Math.imul(al4, bl9)) | 0; + mid = (mid + Math.imul(al4, bh9)) | 0; + mid = (mid + Math.imul(ah4, bl9)) | 0; + hi = (hi + Math.imul(ah4, bh9)) | 0; + var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; + w13 &= 0x3ffffff; + /* k = 14 */ + lo = Math.imul(al9, bl5); + mid = Math.imul(al9, bh5); + mid = (mid + Math.imul(ah9, bl5)) | 0; + hi = Math.imul(ah9, bh5); + lo = (lo + Math.imul(al8, bl6)) | 0; + mid = (mid + Math.imul(al8, bh6)) | 0; + mid = (mid + Math.imul(ah8, bl6)) | 0; + hi = (hi + Math.imul(ah8, bh6)) | 0; + lo = (lo + Math.imul(al7, bl7)) | 0; + mid = (mid + Math.imul(al7, bh7)) | 0; + mid = (mid + Math.imul(ah7, bl7)) | 0; + hi = (hi + Math.imul(ah7, bh7)) | 0; + lo = (lo + Math.imul(al6, bl8)) | 0; + mid = (mid + Math.imul(al6, bh8)) | 0; + mid = (mid + Math.imul(ah6, bl8)) | 0; + hi = (hi + Math.imul(ah6, bh8)) | 0; + lo = (lo + Math.imul(al5, bl9)) | 0; + mid = (mid + Math.imul(al5, bh9)) | 0; + mid = (mid + Math.imul(ah5, bl9)) | 0; + hi = (hi + Math.imul(ah5, bh9)) | 0; + var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; + w14 &= 0x3ffffff; + /* k = 15 */ + lo = Math.imul(al9, bl6); + mid = Math.imul(al9, bh6); + mid = (mid + Math.imul(ah9, bl6)) | 0; + hi = Math.imul(ah9, bh6); + lo = (lo + Math.imul(al8, bl7)) | 0; + mid = (mid + Math.imul(al8, bh7)) | 0; + mid = (mid + Math.imul(ah8, bl7)) | 0; + hi = (hi + Math.imul(ah8, bh7)) | 0; + lo = (lo + Math.imul(al7, bl8)) | 0; + mid = (mid + Math.imul(al7, bh8)) | 0; + mid = (mid + Math.imul(ah7, bl8)) | 0; + hi = (hi + Math.imul(ah7, bh8)) | 0; + lo = (lo + Math.imul(al6, bl9)) | 0; + mid = (mid + Math.imul(al6, bh9)) | 0; + mid = (mid + Math.imul(ah6, bl9)) | 0; + hi = (hi + Math.imul(ah6, bh9)) | 0; + var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; + w15 &= 0x3ffffff; + /* k = 16 */ + lo = Math.imul(al9, bl7); + mid = Math.imul(al9, bh7); + mid = (mid + Math.imul(ah9, bl7)) | 0; + hi = Math.imul(ah9, bh7); + lo = (lo + Math.imul(al8, bl8)) | 0; + mid = (mid + Math.imul(al8, bh8)) | 0; + mid = (mid + Math.imul(ah8, bl8)) | 0; + hi = (hi + Math.imul(ah8, bh8)) | 0; + lo = (lo + Math.imul(al7, bl9)) | 0; + mid = (mid + Math.imul(al7, bh9)) | 0; + mid = (mid + Math.imul(ah7, bl9)) | 0; + hi = (hi + Math.imul(ah7, bh9)) | 0; + var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; + w16 &= 0x3ffffff; + /* k = 17 */ + lo = Math.imul(al9, bl8); + mid = Math.imul(al9, bh8); + mid = (mid + Math.imul(ah9, bl8)) | 0; + hi = Math.imul(ah9, bh8); + lo = (lo + Math.imul(al8, bl9)) | 0; + mid = (mid + Math.imul(al8, bh9)) | 0; + mid = (mid + Math.imul(ah8, bl9)) | 0; + hi = (hi + Math.imul(ah8, bh9)) | 0; + var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; + w17 &= 0x3ffffff; + /* k = 18 */ + lo = Math.imul(al9, bl9); + mid = Math.imul(al9, bh9); + mid = (mid + Math.imul(ah9, bl9)) | 0; + hi = Math.imul(ah9, bh9); + var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; + w18 &= 0x3ffffff; + o[0] = w0; + o[1] = w1; + o[2] = w2; + o[3] = w3; + o[4] = w4; + o[5] = w5; + o[6] = w6; + o[7] = w7; + o[8] = w8; + o[9] = w9; + o[10] = w10; + o[11] = w11; + o[12] = w12; + o[13] = w13; + o[14] = w14; + o[15] = w15; + o[16] = w16; + o[17] = w17; + o[18] = w18; + if (c !== 0) { + o[19] = c; + out.length++; + } + return out; + }; + + // Polyfill comb + if (!Math.imul) { + comb10MulTo = smallMulTo; + } + + function bigMulTo (self, num, out) { + out.negative = num.negative ^ self.negative; + out.length = self.length + num.length; + + var carry = 0; + var hncarry = 0; + for (var k = 0; k < out.length - 1; k++) { + // Sum all words with the same `i + j = k` and accumulate `ncarry`, + // note that ncarry could be >= 0x3ffffff + var ncarry = hncarry; + hncarry = 0; + var rword = carry & 0x3ffffff; + var maxJ = Math.min(k, num.length - 1); + for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { + var i = k - j; + var a = self.words[i] | 0; + var b = num.words[j] | 0; + var r = a * b; + + var lo = r & 0x3ffffff; + ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; + lo = (lo + rword) | 0; + rword = lo & 0x3ffffff; + ncarry = (ncarry + (lo >>> 26)) | 0; + + hncarry += ncarry >>> 26; + ncarry &= 0x3ffffff; + } + out.words[k] = rword; + carry = ncarry; + ncarry = hncarry; + } + if (carry !== 0) { + out.words[k] = carry; + } else { + out.length--; + } + + return out.strip(); + } + + function jumboMulTo (self, num, out) { + var fftm = new FFTM(); + return fftm.mulp(self, num, out); + } + + BN.prototype.mulTo = function mulTo (num, out) { + var res; + var len = this.length + num.length; + if (this.length === 10 && num.length === 10) { + res = comb10MulTo(this, num, out); + } else if (len < 63) { + res = smallMulTo(this, num, out); + } else if (len < 1024) { + res = bigMulTo(this, num, out); + } else { + res = jumboMulTo(this, num, out); + } + + return res; + }; + + // Cooley-Tukey algorithm for FFT + // slightly revisited to rely on looping instead of recursion + + function FFTM (x, y) { + this.x = x; + this.y = y; + } + + FFTM.prototype.makeRBT = function makeRBT (N) { + var t = new Array(N); + var l = BN.prototype._countBits(N) - 1; + for (var i = 0; i < N; i++) { + t[i] = this.revBin(i, l, N); + } + + return t; + }; + + // Returns binary-reversed representation of `x` + FFTM.prototype.revBin = function revBin (x, l, N) { + if (x === 0 || x === N - 1) return x; + + var rb = 0; + for (var i = 0; i < l; i++) { + rb |= (x & 1) << (l - i - 1); + x >>= 1; + } + + return rb; + }; + + // Performs "tweedling" phase, therefore 'emulating' + // behaviour of the recursive algorithm + FFTM.prototype.permute = function permute (rbt, rws, iws, rtws, itws, N) { + for (var i = 0; i < N; i++) { + rtws[i] = rws[rbt[i]]; + itws[i] = iws[rbt[i]]; + } + }; + + FFTM.prototype.transform = function transform (rws, iws, rtws, itws, N, rbt) { + this.permute(rbt, rws, iws, rtws, itws, N); + + for (var s = 1; s < N; s <<= 1) { + var l = s << 1; + + var rtwdf = Math.cos(2 * Math.PI / l); + var itwdf = Math.sin(2 * Math.PI / l); + + for (var p = 0; p < N; p += l) { + var rtwdf_ = rtwdf; + var itwdf_ = itwdf; + + for (var j = 0; j < s; j++) { + var re = rtws[p + j]; + var ie = itws[p + j]; + + var ro = rtws[p + j + s]; + var io = itws[p + j + s]; + + var rx = rtwdf_ * ro - itwdf_ * io; + + io = rtwdf_ * io + itwdf_ * ro; + ro = rx; + + rtws[p + j] = re + ro; + itws[p + j] = ie + io; + + rtws[p + j + s] = re - ro; + itws[p + j + s] = ie - io; + + /* jshint maxdepth : false */ + if (j !== l) { + rx = rtwdf * rtwdf_ - itwdf * itwdf_; + + itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; + rtwdf_ = rx; + } + } + } + } + }; + + FFTM.prototype.guessLen13b = function guessLen13b (n, m) { + var N = Math.max(m, n) | 1; + var odd = N & 1; + var i = 0; + for (N = N / 2 | 0; N; N = N >>> 1) { + i++; + } + + return 1 << i + 1 + odd; + }; + + FFTM.prototype.conjugate = function conjugate (rws, iws, N) { + if (N <= 1) return; + + for (var i = 0; i < N / 2; i++) { + var t = rws[i]; + + rws[i] = rws[N - i - 1]; + rws[N - i - 1] = t; + + t = iws[i]; + + iws[i] = -iws[N - i - 1]; + iws[N - i - 1] = -t; + } + }; + + FFTM.prototype.normalize13b = function normalize13b (ws, N) { + var carry = 0; + for (var i = 0; i < N / 2; i++) { + var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + + Math.round(ws[2 * i] / N) + + carry; + + ws[i] = w & 0x3ffffff; + + if (w < 0x4000000) { + carry = 0; + } else { + carry = w / 0x4000000 | 0; + } + } + + return ws; + }; + + FFTM.prototype.convert13b = function convert13b (ws, len, rws, N) { + var carry = 0; + for (var i = 0; i < len; i++) { + carry = carry + (ws[i] | 0); + + rws[2 * i] = carry & 0x1fff; carry = carry >>> 13; + rws[2 * i + 1] = carry & 0x1fff; carry = carry >>> 13; + } + + // Pad with zeroes + for (i = 2 * len; i < N; ++i) { + rws[i] = 0; + } + + assert(carry === 0); + assert((carry & ~0x1fff) === 0); + }; + + FFTM.prototype.stub = function stub (N) { + var ph = new Array(N); + for (var i = 0; i < N; i++) { + ph[i] = 0; + } + + return ph; + }; + + FFTM.prototype.mulp = function mulp (x, y, out) { + var N = 2 * this.guessLen13b(x.length, y.length); + + var rbt = this.makeRBT(N); + + var _ = this.stub(N); + + var rws = new Array(N); + var rwst = new Array(N); + var iwst = new Array(N); + + var nrws = new Array(N); + var nrwst = new Array(N); + var niwst = new Array(N); + + var rmws = out.words; + rmws.length = N; + + this.convert13b(x.words, x.length, rws, N); + this.convert13b(y.words, y.length, nrws, N); + + this.transform(rws, _, rwst, iwst, N, rbt); + this.transform(nrws, _, nrwst, niwst, N, rbt); + + for (var i = 0; i < N; i++) { + var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; + iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; + rwst[i] = rx; + } + + this.conjugate(rwst, iwst, N); + this.transform(rwst, iwst, rmws, _, N, rbt); + this.conjugate(rmws, _, N); + this.normalize13b(rmws, N); + + out.negative = x.negative ^ y.negative; + out.length = x.length + y.length; + return out.strip(); + }; + + // Multiply `this` by `num` + BN.prototype.mul = function mul (num) { + var out = new BN(null); + out.words = new Array(this.length + num.length); + return this.mulTo(num, out); + }; + + // Multiply employing FFT + BN.prototype.mulf = function mulf (num) { + var out = new BN(null); + out.words = new Array(this.length + num.length); + return jumboMulTo(this, num, out); + }; + + // In-place Multiplication + BN.prototype.imul = function imul (num) { + return this.clone().mulTo(num, this); + }; + + BN.prototype.imuln = function imuln (num) { + assert(typeof num === 'number'); + assert(num < 0x4000000); + + // Carry + var carry = 0; + for (var i = 0; i < this.length; i++) { + var w = (this.words[i] | 0) * num; + var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); + carry >>= 26; + carry += (w / 0x4000000) | 0; + // NOTE: lo is 27bit maximum + carry += lo >>> 26; + this.words[i] = lo & 0x3ffffff; + } + + if (carry !== 0) { + this.words[i] = carry; + this.length++; + } + + return this; + }; + + BN.prototype.muln = function muln (num) { + return this.clone().imuln(num); + }; + + // `this` * `this` + BN.prototype.sqr = function sqr () { + return this.mul(this); + }; + + // `this` * `this` in-place + BN.prototype.isqr = function isqr () { + return this.imul(this.clone()); + }; + + // Math.pow(`this`, `num`) + BN.prototype.pow = function pow (num) { + var w = toBitArray(num); + if (w.length === 0) return new BN(1); + + // Skip leading zeroes + var res = this; + for (var i = 0; i < w.length; i++, res = res.sqr()) { + if (w[i] !== 0) break; + } + + if (++i < w.length) { + for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { + if (w[i] === 0) continue; + + res = res.mul(q); + } + } + + return res; + }; + + // Shift-left in-place + BN.prototype.iushln = function iushln (bits) { + assert(typeof bits === 'number' && bits >= 0); + var r = bits % 26; + var s = (bits - r) / 26; + var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); + var i; + + if (r !== 0) { + var carry = 0; + + for (i = 0; i < this.length; i++) { + var newCarry = this.words[i] & carryMask; + var c = ((this.words[i] | 0) - newCarry) << r; + this.words[i] = c | carry; + carry = newCarry >>> (26 - r); + } + + if (carry) { + this.words[i] = carry; + this.length++; + } + } + + if (s !== 0) { + for (i = this.length - 1; i >= 0; i--) { + this.words[i + s] = this.words[i]; + } + + for (i = 0; i < s; i++) { + this.words[i] = 0; + } + + this.length += s; + } + + return this.strip(); + }; + + BN.prototype.ishln = function ishln (bits) { + // TODO(indutny): implement me + assert(this.negative === 0); + return this.iushln(bits); + }; + + // Shift-right in-place + // NOTE: `hint` is a lowest bit before trailing zeroes + // NOTE: if `extended` is present - it will be filled with destroyed bits + BN.prototype.iushrn = function iushrn (bits, hint, extended) { + assert(typeof bits === 'number' && bits >= 0); + var h; + if (hint) { + h = (hint - (hint % 26)) / 26; + } else { + h = 0; + } + + var r = bits % 26; + var s = Math.min((bits - r) / 26, this.length); + var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); + var maskedWords = extended; + + h -= s; + h = Math.max(0, h); + + // Extended mode, copy masked part + if (maskedWords) { + for (var i = 0; i < s; i++) { + maskedWords.words[i] = this.words[i]; + } + maskedWords.length = s; + } + + if (s === 0) { + // No-op, we should not move anything at all + } else if (this.length > s) { + this.length -= s; + for (i = 0; i < this.length; i++) { + this.words[i] = this.words[i + s]; + } + } else { + this.words[0] = 0; + this.length = 1; + } + + var carry = 0; + for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { + var word = this.words[i] | 0; + this.words[i] = (carry << (26 - r)) | (word >>> r); + carry = word & mask; + } + + // Push carried bits as a mask + if (maskedWords && carry !== 0) { + maskedWords.words[maskedWords.length++] = carry; + } + + if (this.length === 0) { + this.words[0] = 0; + this.length = 1; + } + + return this.strip(); + }; + + BN.prototype.ishrn = function ishrn (bits, hint, extended) { + // TODO(indutny): implement me + assert(this.negative === 0); + return this.iushrn(bits, hint, extended); + }; + + // Shift-left + BN.prototype.shln = function shln (bits) { + return this.clone().ishln(bits); + }; + + BN.prototype.ushln = function ushln (bits) { + return this.clone().iushln(bits); + }; + + // Shift-right + BN.prototype.shrn = function shrn (bits) { + return this.clone().ishrn(bits); + }; + + BN.prototype.ushrn = function ushrn (bits) { + return this.clone().iushrn(bits); + }; + + // Test if n bit is set + BN.prototype.testn = function testn (bit) { + assert(typeof bit === 'number' && bit >= 0); + var r = bit % 26; + var s = (bit - r) / 26; + var q = 1 << r; + + // Fast case: bit is much higher than all existing words + if (this.length <= s) return false; + + // Check bit and return + var w = this.words[s]; + + return !!(w & q); + }; + + // Return only lowers bits of number (in-place) + BN.prototype.imaskn = function imaskn (bits) { + assert(typeof bits === 'number' && bits >= 0); + var r = bits % 26; + var s = (bits - r) / 26; + + assert(this.negative === 0, 'imaskn works only with positive numbers'); + + if (this.length <= s) { + return this; + } + + if (r !== 0) { + s++; + } + this.length = Math.min(s, this.length); + + if (r !== 0) { + var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); + this.words[this.length - 1] &= mask; + } + + return this.strip(); + }; + + // Return only lowers bits of number + BN.prototype.maskn = function maskn (bits) { + return this.clone().imaskn(bits); + }; + + // Add plain number `num` to `this` + BN.prototype.iaddn = function iaddn (num) { + assert(typeof num === 'number'); + assert(num < 0x4000000); + if (num < 0) return this.isubn(-num); + + // Possible sign change + if (this.negative !== 0) { + if (this.length === 1 && (this.words[0] | 0) < num) { + this.words[0] = num - (this.words[0] | 0); + this.negative = 0; + return this; + } + + this.negative = 0; + this.isubn(num); + this.negative = 1; + return this; + } + + // Add without checks + return this._iaddn(num); + }; + + BN.prototype._iaddn = function _iaddn (num) { + this.words[0] += num; + + // Carry + for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { + this.words[i] -= 0x4000000; + if (i === this.length - 1) { + this.words[i + 1] = 1; + } else { + this.words[i + 1]++; + } + } + this.length = Math.max(this.length, i + 1); + + return this; + }; + + // Subtract plain number `num` from `this` + BN.prototype.isubn = function isubn (num) { + assert(typeof num === 'number'); + assert(num < 0x4000000); + if (num < 0) return this.iaddn(-num); + + if (this.negative !== 0) { + this.negative = 0; + this.iaddn(num); + this.negative = 1; + return this; + } + + this.words[0] -= num; + + if (this.length === 1 && this.words[0] < 0) { + this.words[0] = -this.words[0]; + this.negative = 1; + } else { + // Carry + for (var i = 0; i < this.length && this.words[i] < 0; i++) { + this.words[i] += 0x4000000; + this.words[i + 1] -= 1; + } + } + + return this.strip(); + }; + + BN.prototype.addn = function addn (num) { + return this.clone().iaddn(num); + }; + + BN.prototype.subn = function subn (num) { + return this.clone().isubn(num); + }; + + BN.prototype.iabs = function iabs () { + this.negative = 0; + + return this; + }; + + BN.prototype.abs = function abs () { + return this.clone().iabs(); + }; + + BN.prototype._ishlnsubmul = function _ishlnsubmul (num, mul, shift) { + var len = num.length + shift; + var i; + + this._expand(len); + + var w; + var carry = 0; + for (i = 0; i < num.length; i++) { + w = (this.words[i + shift] | 0) + carry; + var right = (num.words[i] | 0) * mul; + w -= right & 0x3ffffff; + carry = (w >> 26) - ((right / 0x4000000) | 0); + this.words[i + shift] = w & 0x3ffffff; + } + for (; i < this.length - shift; i++) { + w = (this.words[i + shift] | 0) + carry; + carry = w >> 26; + this.words[i + shift] = w & 0x3ffffff; + } + + if (carry === 0) return this.strip(); + + // Subtraction overflow + assert(carry === -1); + carry = 0; + for (i = 0; i < this.length; i++) { + w = -(this.words[i] | 0) + carry; + carry = w >> 26; + this.words[i] = w & 0x3ffffff; + } + this.negative = 1; + + return this.strip(); + }; + + BN.prototype._wordDiv = function _wordDiv (num, mode) { + var shift = this.length - num.length; + + var a = this.clone(); + var b = num; + + // Normalize + var bhi = b.words[b.length - 1] | 0; + var bhiBits = this._countBits(bhi); + shift = 26 - bhiBits; + if (shift !== 0) { + b = b.ushln(shift); + a.iushln(shift); + bhi = b.words[b.length - 1] | 0; + } + + // Initialize quotient + var m = a.length - b.length; + var q; + + if (mode !== 'mod') { + q = new BN(null); + q.length = m + 1; + q.words = new Array(q.length); + for (var i = 0; i < q.length; i++) { + q.words[i] = 0; + } + } + + var diff = a.clone()._ishlnsubmul(b, 1, m); + if (diff.negative === 0) { + a = diff; + if (q) { + q.words[m] = 1; + } + } + + for (var j = m - 1; j >= 0; j--) { + var qj = (a.words[b.length + j] | 0) * 0x4000000 + + (a.words[b.length + j - 1] | 0); + + // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max + // (0x7ffffff) + qj = Math.min((qj / bhi) | 0, 0x3ffffff); + + a._ishlnsubmul(b, qj, j); + while (a.negative !== 0) { + qj--; + a.negative = 0; + a._ishlnsubmul(b, 1, j); + if (!a.isZero()) { + a.negative ^= 1; + } + } + if (q) { + q.words[j] = qj; + } + } + if (q) { + q.strip(); + } + a.strip(); + + // Denormalize + if (mode !== 'div' && shift !== 0) { + a.iushrn(shift); + } + + return { + div: q || null, + mod: a + }; + }; + + // NOTE: 1) `mode` can be set to `mod` to request mod only, + // to `div` to request div only, or be absent to + // request both div & mod + // 2) `positive` is true if unsigned mod is requested + BN.prototype.divmod = function divmod (num, mode, positive) { + assert(!num.isZero()); + + if (this.isZero()) { + return { + div: new BN(0), + mod: new BN(0) + }; + } + + var div, mod, res; + if (this.negative !== 0 && num.negative === 0) { + res = this.neg().divmod(num, mode); + + if (mode !== 'mod') { + div = res.div.neg(); + } + + if (mode !== 'div') { + mod = res.mod.neg(); + if (positive && mod.negative !== 0) { + mod.iadd(num); + } + } + + return { + div: div, + mod: mod + }; + } + + if (this.negative === 0 && num.negative !== 0) { + res = this.divmod(num.neg(), mode); + + if (mode !== 'mod') { + div = res.div.neg(); + } + + return { + div: div, + mod: res.mod + }; + } + + if ((this.negative & num.negative) !== 0) { + res = this.neg().divmod(num.neg(), mode); + + if (mode !== 'div') { + mod = res.mod.neg(); + if (positive && mod.negative !== 0) { + mod.isub(num); + } + } + + return { + div: res.div, + mod: mod + }; + } + + // Both numbers are positive at this point + + // Strip both numbers to approximate shift value + if (num.length > this.length || this.cmp(num) < 0) { + return { + div: new BN(0), + mod: this + }; + } + + // Very short reduction + if (num.length === 1) { + if (mode === 'div') { + return { + div: this.divn(num.words[0]), + mod: null + }; + } + + if (mode === 'mod') { + return { + div: null, + mod: new BN(this.modn(num.words[0])) + }; + } + + return { + div: this.divn(num.words[0]), + mod: new BN(this.modn(num.words[0])) + }; + } + + return this._wordDiv(num, mode); + }; + + // Find `this` / `num` + BN.prototype.div = function div (num) { + return this.divmod(num, 'div', false).div; + }; + + // Find `this` % `num` + BN.prototype.mod = function mod (num) { + return this.divmod(num, 'mod', false).mod; + }; + + BN.prototype.umod = function umod (num) { + return this.divmod(num, 'mod', true).mod; + }; + + // Find Round(`this` / `num`) + BN.prototype.divRound = function divRound (num) { + var dm = this.divmod(num); + + // Fast case - exact division + if (dm.mod.isZero()) return dm.div; + + var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; + + var half = num.ushrn(1); + var r2 = num.andln(1); + var cmp = mod.cmp(half); + + // Round down + if (cmp < 0 || r2 === 1 && cmp === 0) return dm.div; + + // Round up + return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); + }; + + BN.prototype.modn = function modn (num) { + assert(num <= 0x3ffffff); + var p = (1 << 26) % num; + + var acc = 0; + for (var i = this.length - 1; i >= 0; i--) { + acc = (p * acc + (this.words[i] | 0)) % num; + } + + return acc; + }; + + // In-place division by number + BN.prototype.idivn = function idivn (num) { + assert(num <= 0x3ffffff); + + var carry = 0; + for (var i = this.length - 1; i >= 0; i--) { + var w = (this.words[i] | 0) + carry * 0x4000000; + this.words[i] = (w / num) | 0; + carry = w % num; + } + + return this.strip(); + }; + + BN.prototype.divn = function divn (num) { + return this.clone().idivn(num); + }; + + BN.prototype.egcd = function egcd (p) { + assert(p.negative === 0); + assert(!p.isZero()); + + var x = this; + var y = p.clone(); + + if (x.negative !== 0) { + x = x.umod(p); + } else { + x = x.clone(); + } + + // A * x + B * y = x + var A = new BN(1); + var B = new BN(0); + + // C * x + D * y = y + var C = new BN(0); + var D = new BN(1); + + var g = 0; + + while (x.isEven() && y.isEven()) { + x.iushrn(1); + y.iushrn(1); + ++g; + } + + var yp = y.clone(); + var xp = x.clone(); + + while (!x.isZero()) { + for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); + if (i > 0) { + x.iushrn(i); + while (i-- > 0) { + if (A.isOdd() || B.isOdd()) { + A.iadd(yp); + B.isub(xp); + } + + A.iushrn(1); + B.iushrn(1); + } + } + + for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); + if (j > 0) { + y.iushrn(j); + while (j-- > 0) { + if (C.isOdd() || D.isOdd()) { + C.iadd(yp); + D.isub(xp); + } + + C.iushrn(1); + D.iushrn(1); + } + } + + if (x.cmp(y) >= 0) { + x.isub(y); + A.isub(C); + B.isub(D); + } else { + y.isub(x); + C.isub(A); + D.isub(B); + } + } + + return { + a: C, + b: D, + gcd: y.iushln(g) + }; + }; + + // This is reduced incarnation of the binary EEA + // above, designated to invert members of the + // _prime_ fields F(p) at a maximal speed + BN.prototype._invmp = function _invmp (p) { + assert(p.negative === 0); + assert(!p.isZero()); + + var a = this; + var b = p.clone(); + + if (a.negative !== 0) { + a = a.umod(p); + } else { + a = a.clone(); + } + + var x1 = new BN(1); + var x2 = new BN(0); + + var delta = b.clone(); + + while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { + for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); + if (i > 0) { + a.iushrn(i); + while (i-- > 0) { + if (x1.isOdd()) { + x1.iadd(delta); + } + + x1.iushrn(1); + } + } + + for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); + if (j > 0) { + b.iushrn(j); + while (j-- > 0) { + if (x2.isOdd()) { + x2.iadd(delta); + } + + x2.iushrn(1); + } + } + + if (a.cmp(b) >= 0) { + a.isub(b); + x1.isub(x2); + } else { + b.isub(a); + x2.isub(x1); + } + } + + var res; + if (a.cmpn(1) === 0) { + res = x1; + } else { + res = x2; + } + + if (res.cmpn(0) < 0) { + res.iadd(p); + } + + return res; + }; + + BN.prototype.gcd = function gcd (num) { + if (this.isZero()) return num.abs(); + if (num.isZero()) return this.abs(); + + var a = this.clone(); + var b = num.clone(); + a.negative = 0; + b.negative = 0; + + // Remove common factor of two + for (var shift = 0; a.isEven() && b.isEven(); shift++) { + a.iushrn(1); + b.iushrn(1); + } + + do { + while (a.isEven()) { + a.iushrn(1); + } + while (b.isEven()) { + b.iushrn(1); + } + + var r = a.cmp(b); + if (r < 0) { + // Swap `a` and `b` to make `a` always bigger than `b` + var t = a; + a = b; + b = t; + } else if (r === 0 || b.cmpn(1) === 0) { + break; + } + + a.isub(b); + } while (true); + + return b.iushln(shift); + }; + + // Invert number in the field F(num) + BN.prototype.invm = function invm (num) { + return this.egcd(num).a.umod(num); + }; + + BN.prototype.isEven = function isEven () { + return (this.words[0] & 1) === 0; + }; + + BN.prototype.isOdd = function isOdd () { + return (this.words[0] & 1) === 1; + }; + + // And first word and num + BN.prototype.andln = function andln (num) { + return this.words[0] & num; + }; + + // Increment at the bit position in-line + BN.prototype.bincn = function bincn (bit) { + assert(typeof bit === 'number'); + var r = bit % 26; + var s = (bit - r) / 26; + var q = 1 << r; + + // Fast case: bit is much higher than all existing words + if (this.length <= s) { + this._expand(s + 1); + this.words[s] |= q; + return this; + } + + // Add bit and propagate, if needed + var carry = q; + for (var i = s; carry !== 0 && i < this.length; i++) { + var w = this.words[i] | 0; + w += carry; + carry = w >>> 26; + w &= 0x3ffffff; + this.words[i] = w; + } + if (carry !== 0) { + this.words[i] = carry; + this.length++; + } + return this; + }; + + BN.prototype.isZero = function isZero () { + return this.length === 1 && this.words[0] === 0; + }; + + BN.prototype.cmpn = function cmpn (num) { + var negative = num < 0; + + if (this.negative !== 0 && !negative) return -1; + if (this.negative === 0 && negative) return 1; + + this.strip(); + + var res; + if (this.length > 1) { + res = 1; + } else { + if (negative) { + num = -num; + } + + assert(num <= 0x3ffffff, 'Number is too big'); + + var w = this.words[0] | 0; + res = w === num ? 0 : w < num ? -1 : 1; + } + if (this.negative !== 0) return -res | 0; + return res; + }; + + // Compare two numbers and return: + // 1 - if `this` > `num` + // 0 - if `this` == `num` + // -1 - if `this` < `num` + BN.prototype.cmp = function cmp (num) { + if (this.negative !== 0 && num.negative === 0) return -1; + if (this.negative === 0 && num.negative !== 0) return 1; + + var res = this.ucmp(num); + if (this.negative !== 0) return -res | 0; + return res; + }; + + // Unsigned comparison + BN.prototype.ucmp = function ucmp (num) { + // At this point both numbers have the same sign + if (this.length > num.length) return 1; + if (this.length < num.length) return -1; + + var res = 0; + for (var i = this.length - 1; i >= 0; i--) { + var a = this.words[i] | 0; + var b = num.words[i] | 0; + + if (a === b) continue; + if (a < b) { + res = -1; + } else if (a > b) { + res = 1; + } + break; + } + return res; + }; + + BN.prototype.gtn = function gtn (num) { + return this.cmpn(num) === 1; + }; + + BN.prototype.gt = function gt (num) { + return this.cmp(num) === 1; + }; + + BN.prototype.gten = function gten (num) { + return this.cmpn(num) >= 0; + }; + + BN.prototype.gte = function gte (num) { + return this.cmp(num) >= 0; + }; + + BN.prototype.ltn = function ltn (num) { + return this.cmpn(num) === -1; + }; + + BN.prototype.lt = function lt (num) { + return this.cmp(num) === -1; + }; + + BN.prototype.lten = function lten (num) { + return this.cmpn(num) <= 0; + }; + + BN.prototype.lte = function lte (num) { + return this.cmp(num) <= 0; + }; + + BN.prototype.eqn = function eqn (num) { + return this.cmpn(num) === 0; + }; + + BN.prototype.eq = function eq (num) { + return this.cmp(num) === 0; + }; + + // + // A reduce context, could be using montgomery or something better, depending + // on the `m` itself. + // + BN.red = function red (num) { + return new Red(num); + }; + + BN.prototype.toRed = function toRed (ctx) { + assert(!this.red, 'Already a number in reduction context'); + assert(this.negative === 0, 'red works only with positives'); + return ctx.convertTo(this)._forceRed(ctx); + }; + + BN.prototype.fromRed = function fromRed () { + assert(this.red, 'fromRed works only with numbers in reduction context'); + return this.red.convertFrom(this); + }; + + BN.prototype._forceRed = function _forceRed (ctx) { + this.red = ctx; + return this; + }; + + BN.prototype.forceRed = function forceRed (ctx) { + assert(!this.red, 'Already a number in reduction context'); + return this._forceRed(ctx); + }; + + BN.prototype.redAdd = function redAdd (num) { + assert(this.red, 'redAdd works only with red numbers'); + return this.red.add(this, num); + }; + + BN.prototype.redIAdd = function redIAdd (num) { + assert(this.red, 'redIAdd works only with red numbers'); + return this.red.iadd(this, num); + }; + + BN.prototype.redSub = function redSub (num) { + assert(this.red, 'redSub works only with red numbers'); + return this.red.sub(this, num); + }; + + BN.prototype.redISub = function redISub (num) { + assert(this.red, 'redISub works only with red numbers'); + return this.red.isub(this, num); + }; + + BN.prototype.redShl = function redShl (num) { + assert(this.red, 'redShl works only with red numbers'); + return this.red.shl(this, num); + }; + + BN.prototype.redMul = function redMul (num) { + assert(this.red, 'redMul works only with red numbers'); + this.red._verify2(this, num); + return this.red.mul(this, num); + }; + + BN.prototype.redIMul = function redIMul (num) { + assert(this.red, 'redMul works only with red numbers'); + this.red._verify2(this, num); + return this.red.imul(this, num); + }; + + BN.prototype.redSqr = function redSqr () { + assert(this.red, 'redSqr works only with red numbers'); + this.red._verify1(this); + return this.red.sqr(this); + }; + + BN.prototype.redISqr = function redISqr () { + assert(this.red, 'redISqr works only with red numbers'); + this.red._verify1(this); + return this.red.isqr(this); + }; + + // Square root over p + BN.prototype.redSqrt = function redSqrt () { + assert(this.red, 'redSqrt works only with red numbers'); + this.red._verify1(this); + return this.red.sqrt(this); + }; + + BN.prototype.redInvm = function redInvm () { + assert(this.red, 'redInvm works only with red numbers'); + this.red._verify1(this); + return this.red.invm(this); + }; + + // Return negative clone of `this` % `red modulo` + BN.prototype.redNeg = function redNeg () { + assert(this.red, 'redNeg works only with red numbers'); + this.red._verify1(this); + return this.red.neg(this); + }; + + BN.prototype.redPow = function redPow (num) { + assert(this.red && !num.red, 'redPow(normalNum)'); + this.red._verify1(this); + return this.red.pow(this, num); + }; + + // Prime numbers with efficient reduction + var primes = { + k256: null, + p224: null, + p192: null, + p25519: null + }; + + // Pseudo-Mersenne prime + function MPrime (name, p) { + // P = 2 ^ N - K + this.name = name; + this.p = new BN(p, 16); + this.n = this.p.bitLength(); + this.k = new BN(1).iushln(this.n).isub(this.p); + + this.tmp = this._tmp(); + } + + MPrime.prototype._tmp = function _tmp () { + var tmp = new BN(null); + tmp.words = new Array(Math.ceil(this.n / 13)); + return tmp; + }; + + MPrime.prototype.ireduce = function ireduce (num) { + // Assumes that `num` is less than `P^2` + // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) + var r = num; + var rlen; + + do { + this.split(r, this.tmp); + r = this.imulK(r); + r = r.iadd(this.tmp); + rlen = r.bitLength(); + } while (rlen > this.n); + + var cmp = rlen < this.n ? -1 : r.ucmp(this.p); + if (cmp === 0) { + r.words[0] = 0; + r.length = 1; + } else if (cmp > 0) { + r.isub(this.p); + } else { + if (r.strip !== undefined) { + // r is BN v4 instance + r.strip(); + } else { + // r is BN v5 instance + r._strip(); + } + } + + return r; + }; + + MPrime.prototype.split = function split (input, out) { + input.iushrn(this.n, 0, out); + }; + + MPrime.prototype.imulK = function imulK (num) { + return num.imul(this.k); + }; + + function K256 () { + MPrime.call( + this, + 'k256', + 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f'); + } + inherits(K256, MPrime); + + K256.prototype.split = function split (input, output) { + // 256 = 9 * 26 + 22 + var mask = 0x3fffff; + + var outLen = Math.min(input.length, 9); + for (var i = 0; i < outLen; i++) { + output.words[i] = input.words[i]; + } + output.length = outLen; + + if (input.length <= 9) { + input.words[0] = 0; + input.length = 1; + return; + } + + // Shift by 9 limbs + var prev = input.words[9]; + output.words[output.length++] = prev & mask; + + for (i = 10; i < input.length; i++) { + var next = input.words[i] | 0; + input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); + prev = next; + } + prev >>>= 22; + input.words[i - 10] = prev; + if (prev === 0 && input.length > 10) { + input.length -= 10; + } else { + input.length -= 9; + } + }; + + K256.prototype.imulK = function imulK (num) { + // K = 0x1000003d1 = [ 0x40, 0x3d1 ] + num.words[num.length] = 0; + num.words[num.length + 1] = 0; + num.length += 2; + + // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 + var lo = 0; + for (var i = 0; i < num.length; i++) { + var w = num.words[i] | 0; + lo += w * 0x3d1; + num.words[i] = lo & 0x3ffffff; + lo = w * 0x40 + ((lo / 0x4000000) | 0); + } + + // Fast length reduction + if (num.words[num.length - 1] === 0) { + num.length--; + if (num.words[num.length - 1] === 0) { + num.length--; + } + } + return num; + }; + + function P224 () { + MPrime.call( + this, + 'p224', + 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001'); + } + inherits(P224, MPrime); + + function P192 () { + MPrime.call( + this, + 'p192', + 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff'); + } + inherits(P192, MPrime); + + function P25519 () { + // 2 ^ 255 - 19 + MPrime.call( + this, + '25519', + '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed'); + } + inherits(P25519, MPrime); + + P25519.prototype.imulK = function imulK (num) { + // K = 0x13 + var carry = 0; + for (var i = 0; i < num.length; i++) { + var hi = (num.words[i] | 0) * 0x13 + carry; + var lo = hi & 0x3ffffff; + hi >>>= 26; + + num.words[i] = lo; + carry = hi; + } + if (carry !== 0) { + num.words[num.length++] = carry; + } + return num; + }; + + // Exported mostly for testing purposes, use plain name instead + BN._prime = function prime (name) { + // Cached version of prime + if (primes[name]) return primes[name]; + + var prime; + if (name === 'k256') { + prime = new K256(); + } else if (name === 'p224') { + prime = new P224(); + } else if (name === 'p192') { + prime = new P192(); + } else if (name === 'p25519') { + prime = new P25519(); + } else { + throw new Error('Unknown prime ' + name); + } + primes[name] = prime; + + return prime; + }; + + // + // Base reduction engine + // + function Red (m) { + if (typeof m === 'string') { + var prime = BN._prime(m); + this.m = prime.p; + this.prime = prime; + } else { + assert(m.gtn(1), 'modulus must be greater than 1'); + this.m = m; + this.prime = null; + } + } + + Red.prototype._verify1 = function _verify1 (a) { + assert(a.negative === 0, 'red works only with positives'); + assert(a.red, 'red works only with red numbers'); + }; + + Red.prototype._verify2 = function _verify2 (a, b) { + assert((a.negative | b.negative) === 0, 'red works only with positives'); + assert(a.red && a.red === b.red, + 'red works only with red numbers'); + }; + + Red.prototype.imod = function imod (a) { + if (this.prime) return this.prime.ireduce(a)._forceRed(this); + return a.umod(this.m)._forceRed(this); + }; + + Red.prototype.neg = function neg (a) { + if (a.isZero()) { + return a.clone(); + } + + return this.m.sub(a)._forceRed(this); + }; + + Red.prototype.add = function add (a, b) { + this._verify2(a, b); + + var res = a.add(b); + if (res.cmp(this.m) >= 0) { + res.isub(this.m); + } + return res._forceRed(this); + }; + + Red.prototype.iadd = function iadd (a, b) { + this._verify2(a, b); + + var res = a.iadd(b); + if (res.cmp(this.m) >= 0) { + res.isub(this.m); + } + return res; + }; + + Red.prototype.sub = function sub (a, b) { + this._verify2(a, b); + + var res = a.sub(b); + if (res.cmpn(0) < 0) { + res.iadd(this.m); + } + return res._forceRed(this); + }; + + Red.prototype.isub = function isub (a, b) { + this._verify2(a, b); + + var res = a.isub(b); + if (res.cmpn(0) < 0) { + res.iadd(this.m); + } + return res; + }; + + Red.prototype.shl = function shl (a, num) { + this._verify1(a); + return this.imod(a.ushln(num)); + }; + + Red.prototype.imul = function imul (a, b) { + this._verify2(a, b); + return this.imod(a.imul(b)); + }; + + Red.prototype.mul = function mul (a, b) { + this._verify2(a, b); + return this.imod(a.mul(b)); + }; + + Red.prototype.isqr = function isqr (a) { + return this.imul(a, a.clone()); + }; + + Red.prototype.sqr = function sqr (a) { + return this.mul(a, a); + }; + + Red.prototype.sqrt = function sqrt (a) { + if (a.isZero()) return a.clone(); + + var mod3 = this.m.andln(3); + assert(mod3 % 2 === 1); + + // Fast case + if (mod3 === 3) { + var pow = this.m.add(new BN(1)).iushrn(2); + return this.pow(a, pow); + } + + // Tonelli-Shanks algorithm (Totally unoptimized and slow) + // + // Find Q and S, that Q * 2 ^ S = (P - 1) + var q = this.m.subn(1); + var s = 0; + while (!q.isZero() && q.andln(1) === 0) { + s++; + q.iushrn(1); + } + assert(!q.isZero()); + + var one = new BN(1).toRed(this); + var nOne = one.redNeg(); + + // Find quadratic non-residue + // NOTE: Max is such because of generalized Riemann hypothesis. + var lpow = this.m.subn(1).iushrn(1); + var z = this.m.bitLength(); + z = new BN(2 * z * z).toRed(this); + + while (this.pow(z, lpow).cmp(nOne) !== 0) { + z.redIAdd(nOne); + } + + var c = this.pow(z, q); + var r = this.pow(a, q.addn(1).iushrn(1)); + var t = this.pow(a, q); + var m = s; + while (t.cmp(one) !== 0) { + var tmp = t; + for (var i = 0; tmp.cmp(one) !== 0; i++) { + tmp = tmp.redSqr(); + } + assert(i < m); + var b = this.pow(c, new BN(1).iushln(m - i - 1)); + + r = r.redMul(b); + c = b.redSqr(); + t = t.redMul(c); + m = i; + } + + return r; + }; + + Red.prototype.invm = function invm (a) { + var inv = a._invmp(this.m); + if (inv.negative !== 0) { + inv.negative = 0; + return this.imod(inv).redNeg(); + } else { + return this.imod(inv); + } + }; + + Red.prototype.pow = function pow (a, num) { + if (num.isZero()) return new BN(1).toRed(this); + if (num.cmpn(1) === 0) return a.clone(); + + var windowSize = 4; + var wnd = new Array(1 << windowSize); + wnd[0] = new BN(1).toRed(this); + wnd[1] = a; + for (var i = 2; i < wnd.length; i++) { + wnd[i] = this.mul(wnd[i - 1], a); + } + + var res = wnd[0]; + var current = 0; + var currentLen = 0; + var start = num.bitLength() % 26; + if (start === 0) { + start = 26; + } + + for (i = num.length - 1; i >= 0; i--) { + var word = num.words[i]; + for (var j = start - 1; j >= 0; j--) { + var bit = (word >> j) & 1; + if (res !== wnd[0]) { + res = this.sqr(res); + } + + if (bit === 0 && current === 0) { + currentLen = 0; + continue; + } + + current <<= 1; + current |= bit; + currentLen++; + if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; + + res = this.mul(res, wnd[current]); + currentLen = 0; + current = 0; + } + start = 26; + } + + return res; + }; + + Red.prototype.convertTo = function convertTo (num) { + var r = num.umod(this.m); + + return r === num ? r.clone() : r; + }; + + Red.prototype.convertFrom = function convertFrom (num) { + var res = num.clone(); + res.red = null; + return res; + }; + + // + // Montgomery method engine + // + + BN.mont = function mont (num) { + return new Mont(num); + }; + + function Mont (m) { + Red.call(this, m); + + this.shift = this.m.bitLength(); + if (this.shift % 26 !== 0) { + this.shift += 26 - (this.shift % 26); + } + + this.r = new BN(1).iushln(this.shift); + this.r2 = this.imod(this.r.sqr()); + this.rinv = this.r._invmp(this.m); + + this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); + this.minv = this.minv.umod(this.r); + this.minv = this.r.sub(this.minv); + } + inherits(Mont, Red); + + Mont.prototype.convertTo = function convertTo (num) { + return this.imod(num.ushln(this.shift)); + }; + + Mont.prototype.convertFrom = function convertFrom (num) { + var r = this.imod(num.mul(this.rinv)); + r.red = null; + return r; + }; + + Mont.prototype.imul = function imul (a, b) { + if (a.isZero() || b.isZero()) { + a.words[0] = 0; + a.length = 1; + return a; + } + + var t = a.imul(b); + var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); + var u = t.isub(c).iushrn(this.shift); + var res = u; + + if (u.cmp(this.m) >= 0) { + res = u.isub(this.m); + } else if (u.cmpn(0) < 0) { + res = u.iadd(this.m); + } + + return res._forceRed(this); + }; + + Mont.prototype.mul = function mul (a, b) { + if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); + + var t = a.mul(b); + var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); + var u = t.isub(c).iushrn(this.shift); + var res = u; + if (u.cmp(this.m) >= 0) { + res = u.isub(this.m); + } else if (u.cmpn(0) < 0) { + res = u.iadd(this.m); + } + + return res._forceRed(this); + }; + + Mont.prototype.invm = function invm (a) { + // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R + var res = this.imod(a._invmp(this.m).mul(this.r2)); + return res._forceRed(this); + }; +})(typeof module === 'undefined' || module, this); + +},{"buffer":292}],125:[function(require,module,exports){ +module.exports={ + "name": "elliptic", + "version": "6.5.4", + "description": "EC cryptography", + "main": "lib/elliptic.js", + "files": [ + "lib" + ], + "scripts": { + "lint": "eslint lib test", + "lint:fix": "npm run lint -- --fix", + "unit": "istanbul test _mocha --reporter=spec test/index.js", + "test": "npm run lint && npm run unit", + "version": "grunt dist && git add dist/" + }, + "repository": { + "type": "git", + "url": "git@github.com:indutny/elliptic" + }, + "keywords": [ + "EC", + "Elliptic", + "curve", + "Cryptography" + ], + "author": "Fedor Indutny ", + "license": "MIT", + "bugs": { + "url": "https://github.com/indutny/elliptic/issues" + }, + "homepage": "https://github.com/indutny/elliptic", + "devDependencies": { + "brfs": "^2.0.2", + "coveralls": "^3.1.0", + "eslint": "^7.6.0", + "grunt": "^1.2.1", + "grunt-browserify": "^5.3.0", + "grunt-cli": "^1.3.2", + "grunt-contrib-connect": "^3.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-uglify": "^5.0.0", + "grunt-mocha-istanbul": "^5.0.2", + "grunt-saucelabs": "^9.0.1", + "istanbul": "^0.4.5", + "mocha": "^8.0.1" + }, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } +} + +},{}],126:[function(require,module,exports){ +'use strict' + +const AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN +const AbstractChainedBatch = require('abstract-leveldown').AbstractChainedBatch +const AbstractIterator = require('abstract-leveldown').AbstractIterator +const inherits = require('inherits') +const Codec = require('level-codec') +const EncodingError = require('level-errors').EncodingError +const rangeMethods = ['approximateSize', 'compactRange'] + +module.exports = DB + +function DB (db, opts) { + if (!(this instanceof DB)) return new DB(db, opts) + + const manifest = db.supports || {} + const additionalMethods = manifest.additionalMethods || {} + + AbstractLevelDOWN.call(this, manifest) + + this.supports.encodings = true + this.supports.additionalMethods = {} + + rangeMethods.forEach(function (m) { + // TODO (future major): remove this fallback + const fallback = typeof db[m] === 'function' + + if (additionalMethods[m] || fallback) { + this.supports.additionalMethods[m] = true + + this[m] = function (start, end, opts, cb) { + start = this.codec.encodeKey(start, opts) + end = this.codec.encodeKey(end, opts) + return this.db[m](start, end, opts, cb) + } + } + }, this) + + opts = opts || {} + if (typeof opts.keyEncoding === 'undefined') opts.keyEncoding = 'utf8' + if (typeof opts.valueEncoding === 'undefined') opts.valueEncoding = 'utf8' + + this.db = db + this.codec = new Codec(opts) +} + +inherits(DB, AbstractLevelDOWN) + +DB.prototype.type = 'encoding-down' + +DB.prototype._serializeKey = +DB.prototype._serializeValue = function (datum) { + return datum +} + +DB.prototype._open = function (opts, cb) { + this.db.open(opts, cb) +} + +DB.prototype._close = function (cb) { + this.db.close(cb) +} + +DB.prototype._put = function (key, value, opts, cb) { + key = this.codec.encodeKey(key, opts) + value = this.codec.encodeValue(value, opts) + this.db.put(key, value, opts, cb) +} + +DB.prototype._get = function (key, opts, cb) { + key = this.codec.encodeKey(key, opts) + opts.asBuffer = this.codec.valueAsBuffer(opts) + + this.db.get(key, opts, (err, value) => { + if (err) return cb(err) + + try { + value = this.codec.decodeValue(value, opts) + } catch (err) { + return cb(new EncodingError(err)) + } + + cb(null, value) + }) +} + +DB.prototype._getMany = function (keys, opts, cb) { + keys = keys.map((key) => this.codec.encodeKey(key, opts)) + opts.asBuffer = this.codec.valueAsBuffer(opts) + + this.db.getMany(keys, opts, (err, values) => { + if (err) return cb(err) + + const decoded = new Array(values.length) + + for (let i = 0; i < values.length; i++) { + if (values[i] === undefined) { + decoded[i] = undefined + continue + } + + try { + decoded[i] = this.codec.decodeValue(values[i], opts) + } catch (err) { + return cb(new EncodingError(err)) + } + } + + cb(null, decoded) + }) +} + +DB.prototype._del = function (key, opts, cb) { + key = this.codec.encodeKey(key, opts) + this.db.del(key, opts, cb) +} + +DB.prototype._chainedBatch = function () { + return new Batch(this) +} + +DB.prototype._batch = function (ops, opts, cb) { + ops = this.codec.encodeBatch(ops, opts) + this.db.batch(ops, opts, cb) +} + +DB.prototype._iterator = function (opts) { + opts.keyAsBuffer = this.codec.keyAsBuffer(opts) + opts.valueAsBuffer = this.codec.valueAsBuffer(opts) + return new Iterator(this, opts) +} + +DB.prototype._clear = function (opts, callback) { + opts = this.codec.encodeLtgt(opts) + this.db.clear(opts, callback) +} + +function Iterator (db, opts) { + AbstractIterator.call(this, db) + this.codec = db.codec + this.keys = opts.keys + this.values = opts.values + this.opts = this.codec.encodeLtgt(opts) + this.it = db.db.iterator(this.opts) +} + +inherits(Iterator, AbstractIterator) + +Iterator.prototype._next = function (cb) { + this.it.next((err, key, value) => { + if (err) return cb(err) + + try { + if (this.keys && typeof key !== 'undefined') { + key = this.codec.decodeKey(key, this.opts) + } else { + key = undefined + } + + if (this.values && typeof value !== 'undefined') { + value = this.codec.decodeValue(value, this.opts) + } else { + value = undefined + } + } catch (err) { + return cb(new EncodingError(err)) + } + + cb(null, key, value) + }) +} + +Iterator.prototype._seek = function (key) { + key = this.codec.encodeKey(key, this.opts) + this.it.seek(key) +} + +Iterator.prototype._end = function (cb) { + this.it.end(cb) +} + +function Batch (db, codec) { + AbstractChainedBatch.call(this, db) + this.codec = db.codec + this.batch = db.db.batch() +} + +inherits(Batch, AbstractChainedBatch) + +Batch.prototype._put = function (key, value, options) { + key = this.codec.encodeKey(key, options) + value = this.codec.encodeValue(value, options) + this.batch.put(key, value) +} + +Batch.prototype._del = function (key, options) { + key = this.codec.encodeKey(key, options) + this.batch.del(key) +} + +Batch.prototype._clear = function () { + this.batch.clear() +} + +Batch.prototype._write = function (opts, cb) { + this.batch.write(opts, cb) +} + +},{"abstract-leveldown":80,"inherits":141,"level-codec":143,"level-errors":145}],127:[function(require,module,exports){ +(function (root, factory) { + // Hack to make all exports of this module sha256 function object properties. + var exports = {}; + factory(exports); + var sha256 = exports["default"]; + for (var k in exports) { + sha256[k] = exports[k]; + } + + if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = sha256; + } else if (typeof define === 'function' && define.amd) { + define(function() { return sha256; }); + } else { + root.sha256 = sha256; + } +})(this, function(exports) { +"use strict"; +exports.__esModule = true; +// SHA-256 (+ HMAC and PBKDF2) for JavaScript. +// +// Written in 2014-2016 by Dmitry Chestnykh. +// Public domain, no warranty. +// +// Functions (accept and return Uint8Arrays): +// +// sha256(message) -> hash +// sha256.hmac(key, message) -> mac +// sha256.pbkdf2(password, salt, rounds, dkLen) -> dk +// +// Classes: +// +// new sha256.Hash() +// new sha256.HMAC(key) +// +exports.digestLength = 32; +exports.blockSize = 64; +// SHA-256 constants +var K = new Uint32Array([ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, + 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, + 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, + 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, + 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, + 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, + 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, + 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, + 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +]); +function hashBlocks(w, v, p, pos, len) { + var a, b, c, d, e, f, g, h, u, i, j, t1, t2; + while (len >= 64) { + a = v[0]; + b = v[1]; + c = v[2]; + d = v[3]; + e = v[4]; + f = v[5]; + g = v[6]; + h = v[7]; + for (i = 0; i < 16; i++) { + j = pos + i * 4; + w[i] = (((p[j] & 0xff) << 24) | ((p[j + 1] & 0xff) << 16) | + ((p[j + 2] & 0xff) << 8) | (p[j + 3] & 0xff)); + } + for (i = 16; i < 64; i++) { + u = w[i - 2]; + t1 = (u >>> 17 | u << (32 - 17)) ^ (u >>> 19 | u << (32 - 19)) ^ (u >>> 10); + u = w[i - 15]; + t2 = (u >>> 7 | u << (32 - 7)) ^ (u >>> 18 | u << (32 - 18)) ^ (u >>> 3); + w[i] = (t1 + w[i - 7] | 0) + (t2 + w[i - 16] | 0); + } + for (i = 0; i < 64; i++) { + t1 = (((((e >>> 6 | e << (32 - 6)) ^ (e >>> 11 | e << (32 - 11)) ^ + (e >>> 25 | e << (32 - 25))) + ((e & f) ^ (~e & g))) | 0) + + ((h + ((K[i] + w[i]) | 0)) | 0)) | 0; + t2 = (((a >>> 2 | a << (32 - 2)) ^ (a >>> 13 | a << (32 - 13)) ^ + (a >>> 22 | a << (32 - 22))) + ((a & b) ^ (a & c) ^ (b & c))) | 0; + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + v[0] += a; + v[1] += b; + v[2] += c; + v[3] += d; + v[4] += e; + v[5] += f; + v[6] += g; + v[7] += h; + pos += 64; + len -= 64; + } + return pos; +} +// Hash implements SHA256 hash algorithm. +var Hash = /** @class */ (function () { + function Hash() { + this.digestLength = exports.digestLength; + this.blockSize = exports.blockSize; + // Note: Int32Array is used instead of Uint32Array for performance reasons. + this.state = new Int32Array(8); // hash state + this.temp = new Int32Array(64); // temporary state + this.buffer = new Uint8Array(128); // buffer for data to hash + this.bufferLength = 0; // number of bytes in buffer + this.bytesHashed = 0; // number of total bytes hashed + this.finished = false; // indicates whether the hash was finalized + this.reset(); + } + // Resets hash state making it possible + // to re-use this instance to hash other data. + Hash.prototype.reset = function () { + this.state[0] = 0x6a09e667; + this.state[1] = 0xbb67ae85; + this.state[2] = 0x3c6ef372; + this.state[3] = 0xa54ff53a; + this.state[4] = 0x510e527f; + this.state[5] = 0x9b05688c; + this.state[6] = 0x1f83d9ab; + this.state[7] = 0x5be0cd19; + this.bufferLength = 0; + this.bytesHashed = 0; + this.finished = false; + return this; + }; + // Cleans internal buffers and re-initializes hash state. + Hash.prototype.clean = function () { + for (var i = 0; i < this.buffer.length; i++) { + this.buffer[i] = 0; + } + for (var i = 0; i < this.temp.length; i++) { + this.temp[i] = 0; + } + this.reset(); + }; + // Updates hash state with the given data. + // + // Optionally, length of the data can be specified to hash + // fewer bytes than data.length. + // + // Throws error when trying to update already finalized hash: + // instance must be reset to use it again. + Hash.prototype.update = function (data, dataLength) { + if (dataLength === void 0) { dataLength = data.length; } + if (this.finished) { + throw new Error("SHA256: can't update because hash was finished."); + } + var dataPos = 0; + this.bytesHashed += dataLength; + if (this.bufferLength > 0) { + while (this.bufferLength < 64 && dataLength > 0) { + this.buffer[this.bufferLength++] = data[dataPos++]; + dataLength--; + } + if (this.bufferLength === 64) { + hashBlocks(this.temp, this.state, this.buffer, 0, 64); + this.bufferLength = 0; + } + } + if (dataLength >= 64) { + dataPos = hashBlocks(this.temp, this.state, data, dataPos, dataLength); + dataLength %= 64; + } + while (dataLength > 0) { + this.buffer[this.bufferLength++] = data[dataPos++]; + dataLength--; + } + return this; + }; + // Finalizes hash state and puts hash into out. + // + // If hash was already finalized, puts the same value. + Hash.prototype.finish = function (out) { + if (!this.finished) { + var bytesHashed = this.bytesHashed; + var left = this.bufferLength; + var bitLenHi = (bytesHashed / 0x20000000) | 0; + var bitLenLo = bytesHashed << 3; + var padLength = (bytesHashed % 64 < 56) ? 64 : 128; + this.buffer[left] = 0x80; + for (var i = left + 1; i < padLength - 8; i++) { + this.buffer[i] = 0; + } + this.buffer[padLength - 8] = (bitLenHi >>> 24) & 0xff; + this.buffer[padLength - 7] = (bitLenHi >>> 16) & 0xff; + this.buffer[padLength - 6] = (bitLenHi >>> 8) & 0xff; + this.buffer[padLength - 5] = (bitLenHi >>> 0) & 0xff; + this.buffer[padLength - 4] = (bitLenLo >>> 24) & 0xff; + this.buffer[padLength - 3] = (bitLenLo >>> 16) & 0xff; + this.buffer[padLength - 2] = (bitLenLo >>> 8) & 0xff; + this.buffer[padLength - 1] = (bitLenLo >>> 0) & 0xff; + hashBlocks(this.temp, this.state, this.buffer, 0, padLength); + this.finished = true; + } + for (var i = 0; i < 8; i++) { + out[i * 4 + 0] = (this.state[i] >>> 24) & 0xff; + out[i * 4 + 1] = (this.state[i] >>> 16) & 0xff; + out[i * 4 + 2] = (this.state[i] >>> 8) & 0xff; + out[i * 4 + 3] = (this.state[i] >>> 0) & 0xff; + } + return this; + }; + // Returns the final hash digest. + Hash.prototype.digest = function () { + var out = new Uint8Array(this.digestLength); + this.finish(out); + return out; + }; + // Internal function for use in HMAC for optimization. + Hash.prototype._saveState = function (out) { + for (var i = 0; i < this.state.length; i++) { + out[i] = this.state[i]; + } + }; + // Internal function for use in HMAC for optimization. + Hash.prototype._restoreState = function (from, bytesHashed) { + for (var i = 0; i < this.state.length; i++) { + this.state[i] = from[i]; + } + this.bytesHashed = bytesHashed; + this.finished = false; + this.bufferLength = 0; + }; + return Hash; +}()); +exports.Hash = Hash; +// HMAC implements HMAC-SHA256 message authentication algorithm. +var HMAC = /** @class */ (function () { + function HMAC(key) { + this.inner = new Hash(); + this.outer = new Hash(); + this.blockSize = this.inner.blockSize; + this.digestLength = this.inner.digestLength; + var pad = new Uint8Array(this.blockSize); + if (key.length > this.blockSize) { + (new Hash()).update(key).finish(pad).clean(); + } + else { + for (var i = 0; i < key.length; i++) { + pad[i] = key[i]; + } + } + for (var i = 0; i < pad.length; i++) { + pad[i] ^= 0x36; + } + this.inner.update(pad); + for (var i = 0; i < pad.length; i++) { + pad[i] ^= 0x36 ^ 0x5c; + } + this.outer.update(pad); + this.istate = new Uint32Array(8); + this.ostate = new Uint32Array(8); + this.inner._saveState(this.istate); + this.outer._saveState(this.ostate); + for (var i = 0; i < pad.length; i++) { + pad[i] = 0; + } + } + // Returns HMAC state to the state initialized with key + // to make it possible to run HMAC over the other data with the same + // key without creating a new instance. + HMAC.prototype.reset = function () { + this.inner._restoreState(this.istate, this.inner.blockSize); + this.outer._restoreState(this.ostate, this.outer.blockSize); + return this; + }; + // Cleans HMAC state. + HMAC.prototype.clean = function () { + for (var i = 0; i < this.istate.length; i++) { + this.ostate[i] = this.istate[i] = 0; + } + this.inner.clean(); + this.outer.clean(); + }; + // Updates state with provided data. + HMAC.prototype.update = function (data) { + this.inner.update(data); + return this; + }; + // Finalizes HMAC and puts the result in out. + HMAC.prototype.finish = function (out) { + if (this.outer.finished) { + this.outer.finish(out); + } + else { + this.inner.finish(out); + this.outer.update(out, this.digestLength).finish(out); + } + return this; + }; + // Returns message authentication code. + HMAC.prototype.digest = function () { + var out = new Uint8Array(this.digestLength); + this.finish(out); + return out; + }; + return HMAC; +}()); +exports.HMAC = HMAC; +// Returns SHA256 hash of data. +function hash(data) { + var h = (new Hash()).update(data); + var digest = h.digest(); + h.clean(); + return digest; +} +exports.hash = hash; +// Function hash is both available as module.hash and as default export. +exports["default"] = hash; +// Returns HMAC-SHA256 of data under the key. +function hmac(key, data) { + var h = (new HMAC(key)).update(data); + var digest = h.digest(); + h.clean(); + return digest; +} +exports.hmac = hmac; +// Fills hkdf buffer like this: +// T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) +function fillBuffer(buffer, hmac, info, counter) { + // Counter is a byte value: check if it overflowed. + var num = counter[0]; + if (num === 0) { + throw new Error("hkdf: cannot expand more"); + } + // Prepare HMAC instance for new data with old key. + hmac.reset(); + // Hash in previous output if it was generated + // (i.e. counter is greater than 1). + if (num > 1) { + hmac.update(buffer); + } + // Hash in info if it exists. + if (info) { + hmac.update(info); + } + // Hash in the counter. + hmac.update(counter); + // Output result to buffer and clean HMAC instance. + hmac.finish(buffer); + // Increment counter inside typed array, this works properly. + counter[0]++; +} +var hkdfSalt = new Uint8Array(exports.digestLength); // Filled with zeroes. +function hkdf(key, salt, info, length) { + if (salt === void 0) { salt = hkdfSalt; } + if (length === void 0) { length = 32; } + var counter = new Uint8Array([1]); + // HKDF-Extract uses salt as HMAC key, and key as data. + var okm = hmac(salt, key); + // Initialize HMAC for expanding with extracted key. + // Ensure no collisions with `hmac` function. + var hmac_ = new HMAC(okm); + // Allocate buffer. + var buffer = new Uint8Array(hmac_.digestLength); + var bufpos = buffer.length; + var out = new Uint8Array(length); + for (var i = 0; i < length; i++) { + if (bufpos === buffer.length) { + fillBuffer(buffer, hmac_, info, counter); + bufpos = 0; + } + out[i] = buffer[bufpos++]; + } + hmac_.clean(); + buffer.fill(0); + counter.fill(0); + return out; +} +exports.hkdf = hkdf; +// Derives a key from password and salt using PBKDF2-HMAC-SHA256 +// with the given number of iterations. +// +// The number of bytes returned is equal to dkLen. +// +// (For better security, avoid dkLen greater than hash length - 32 bytes). +function pbkdf2(password, salt, iterations, dkLen) { + var prf = new HMAC(password); + var len = prf.digestLength; + var ctr = new Uint8Array(4); + var t = new Uint8Array(len); + var u = new Uint8Array(len); + var dk = new Uint8Array(dkLen); + for (var i = 0; i * len < dkLen; i++) { + var c = i + 1; + ctr[0] = (c >>> 24) & 0xff; + ctr[1] = (c >>> 16) & 0xff; + ctr[2] = (c >>> 8) & 0xff; + ctr[3] = (c >>> 0) & 0xff; + prf.reset(); + prf.update(salt); + prf.update(ctr); + prf.finish(u); + for (var j = 0; j < len; j++) { + t[j] = u[j]; + } + for (var j = 2; j <= iterations; j++) { + prf.reset(); + prf.update(u).finish(u); + for (var k = 0; k < len; k++) { + t[k] ^= u[k]; + } + } + for (var j = 0; j < len && i * len + j < dkLen; j++) { + dk[i * len + j] = t[j]; + } + } + for (var i = 0; i < len; i++) { + t[i] = u[i] = 0; + } + for (var i = 0; i < 4; i++) { + ctr[i] = 0; + } + prf.clean(); + return dk; +} +exports.pbkdf2 = pbkdf2; +}); + +},{}],128:[function(require,module,exports){ +var hash = exports; + +hash.utils = require('./hash/utils'); +hash.common = require('./hash/common'); +hash.sha = require('./hash/sha'); +hash.ripemd = require('./hash/ripemd'); +hash.hmac = require('./hash/hmac'); + +// Proxy hash functions to the main object +hash.sha1 = hash.sha.sha1; +hash.sha256 = hash.sha.sha256; +hash.sha224 = hash.sha.sha224; +hash.sha384 = hash.sha.sha384; +hash.sha512 = hash.sha.sha512; +hash.ripemd160 = hash.ripemd.ripemd160; + +},{"./hash/common":129,"./hash/hmac":130,"./hash/ripemd":131,"./hash/sha":132,"./hash/utils":139}],129:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); +var assert = require('minimalistic-assert'); + +function BlockHash() { + this.pending = null; + this.pendingTotal = 0; + this.blockSize = this.constructor.blockSize; + this.outSize = this.constructor.outSize; + this.hmacStrength = this.constructor.hmacStrength; + this.padLength = this.constructor.padLength / 8; + this.endian = 'big'; + + this._delta8 = this.blockSize / 8; + this._delta32 = this.blockSize / 32; +} +exports.BlockHash = BlockHash; + +BlockHash.prototype.update = function update(msg, enc) { + // Convert message to array, pad it, and join into 32bit blocks + msg = utils.toArray(msg, enc); + if (!this.pending) + this.pending = msg; + else + this.pending = this.pending.concat(msg); + this.pendingTotal += msg.length; + + // Enough data, try updating + if (this.pending.length >= this._delta8) { + msg = this.pending; + + // Process pending data in blocks + var r = msg.length % this._delta8; + this.pending = msg.slice(msg.length - r, msg.length); + if (this.pending.length === 0) + this.pending = null; + + msg = utils.join32(msg, 0, msg.length - r, this.endian); + for (var i = 0; i < msg.length; i += this._delta32) + this._update(msg, i, i + this._delta32); + } + + return this; +}; + +BlockHash.prototype.digest = function digest(enc) { + this.update(this._pad()); + assert(this.pending === null); + + return this._digest(enc); +}; + +BlockHash.prototype._pad = function pad() { + var len = this.pendingTotal; + var bytes = this._delta8; + var k = bytes - ((len + this.padLength) % bytes); + var res = new Array(k + this.padLength); + res[0] = 0x80; + for (var i = 1; i < k; i++) + res[i] = 0; + + // Append length + len <<= 3; + if (this.endian === 'big') { + for (var t = 8; t < this.padLength; t++) + res[i++] = 0; + + res[i++] = 0; + res[i++] = 0; + res[i++] = 0; + res[i++] = 0; + res[i++] = (len >>> 24) & 0xff; + res[i++] = (len >>> 16) & 0xff; + res[i++] = (len >>> 8) & 0xff; + res[i++] = len & 0xff; + } else { + res[i++] = len & 0xff; + res[i++] = (len >>> 8) & 0xff; + res[i++] = (len >>> 16) & 0xff; + res[i++] = (len >>> 24) & 0xff; + res[i++] = 0; + res[i++] = 0; + res[i++] = 0; + res[i++] = 0; + + for (t = 8; t < this.padLength; t++) + res[i++] = 0; + } + + return res; +}; + +},{"./utils":139,"minimalistic-assert":163}],130:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); +var assert = require('minimalistic-assert'); + +function Hmac(hash, key, enc) { + if (!(this instanceof Hmac)) + return new Hmac(hash, key, enc); + this.Hash = hash; + this.blockSize = hash.blockSize / 8; + this.outSize = hash.outSize / 8; + this.inner = null; + this.outer = null; + + this._init(utils.toArray(key, enc)); +} +module.exports = Hmac; + +Hmac.prototype._init = function init(key) { + // Shorten key, if needed + if (key.length > this.blockSize) + key = new this.Hash().update(key).digest(); + assert(key.length <= this.blockSize); + + // Add padding to key + for (var i = key.length; i < this.blockSize; i++) + key.push(0); + + for (i = 0; i < key.length; i++) + key[i] ^= 0x36; + this.inner = new this.Hash().update(key); + + // 0x36 ^ 0x5c = 0x6a + for (i = 0; i < key.length; i++) + key[i] ^= 0x6a; + this.outer = new this.Hash().update(key); +}; + +Hmac.prototype.update = function update(msg, enc) { + this.inner.update(msg, enc); + return this; +}; + +Hmac.prototype.digest = function digest(enc) { + this.outer.update(this.inner.digest()); + return this.outer.digest(enc); +}; + +},{"./utils":139,"minimalistic-assert":163}],131:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); +var common = require('./common'); + +var rotl32 = utils.rotl32; +var sum32 = utils.sum32; +var sum32_3 = utils.sum32_3; +var sum32_4 = utils.sum32_4; +var BlockHash = common.BlockHash; + +function RIPEMD160() { + if (!(this instanceof RIPEMD160)) + return new RIPEMD160(); + + BlockHash.call(this); + + this.h = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ]; + this.endian = 'little'; +} +utils.inherits(RIPEMD160, BlockHash); +exports.ripemd160 = RIPEMD160; + +RIPEMD160.blockSize = 512; +RIPEMD160.outSize = 160; +RIPEMD160.hmacStrength = 192; +RIPEMD160.padLength = 64; + +RIPEMD160.prototype._update = function update(msg, start) { + var A = this.h[0]; + var B = this.h[1]; + var C = this.h[2]; + var D = this.h[3]; + var E = this.h[4]; + var Ah = A; + var Bh = B; + var Ch = C; + var Dh = D; + var Eh = E; + for (var j = 0; j < 80; j++) { + var T = sum32( + rotl32( + sum32_4(A, f(j, B, C, D), msg[r[j] + start], K(j)), + s[j]), + E); + A = E; + E = D; + D = rotl32(C, 10); + C = B; + B = T; + T = sum32( + rotl32( + sum32_4(Ah, f(79 - j, Bh, Ch, Dh), msg[rh[j] + start], Kh(j)), + sh[j]), + Eh); + Ah = Eh; + Eh = Dh; + Dh = rotl32(Ch, 10); + Ch = Bh; + Bh = T; + } + T = sum32_3(this.h[1], C, Dh); + this.h[1] = sum32_3(this.h[2], D, Eh); + this.h[2] = sum32_3(this.h[3], E, Ah); + this.h[3] = sum32_3(this.h[4], A, Bh); + this.h[4] = sum32_3(this.h[0], B, Ch); + this.h[0] = T; +}; + +RIPEMD160.prototype._digest = function digest(enc) { + if (enc === 'hex') + return utils.toHex32(this.h, 'little'); + else + return utils.split32(this.h, 'little'); +}; + +function f(j, x, y, z) { + if (j <= 15) + return x ^ y ^ z; + else if (j <= 31) + return (x & y) | ((~x) & z); + else if (j <= 47) + return (x | (~y)) ^ z; + else if (j <= 63) + return (x & z) | (y & (~z)); + else + return x ^ (y | (~z)); +} + +function K(j) { + if (j <= 15) + return 0x00000000; + else if (j <= 31) + return 0x5a827999; + else if (j <= 47) + return 0x6ed9eba1; + else if (j <= 63) + return 0x8f1bbcdc; + else + return 0xa953fd4e; +} + +function Kh(j) { + if (j <= 15) + return 0x50a28be6; + else if (j <= 31) + return 0x5c4dd124; + else if (j <= 47) + return 0x6d703ef3; + else if (j <= 63) + return 0x7a6d76e9; + else + return 0x00000000; +} + +var r = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 +]; + +var rh = [ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 +]; + +var s = [ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 +]; + +var sh = [ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 +]; + +},{"./common":129,"./utils":139}],132:[function(require,module,exports){ +'use strict'; + +exports.sha1 = require('./sha/1'); +exports.sha224 = require('./sha/224'); +exports.sha256 = require('./sha/256'); +exports.sha384 = require('./sha/384'); +exports.sha512 = require('./sha/512'); + +},{"./sha/1":133,"./sha/224":134,"./sha/256":135,"./sha/384":136,"./sha/512":137}],133:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var common = require('../common'); +var shaCommon = require('./common'); + +var rotl32 = utils.rotl32; +var sum32 = utils.sum32; +var sum32_5 = utils.sum32_5; +var ft_1 = shaCommon.ft_1; +var BlockHash = common.BlockHash; + +var sha1_K = [ + 0x5A827999, 0x6ED9EBA1, + 0x8F1BBCDC, 0xCA62C1D6 +]; + +function SHA1() { + if (!(this instanceof SHA1)) + return new SHA1(); + + BlockHash.call(this); + this.h = [ + 0x67452301, 0xefcdab89, 0x98badcfe, + 0x10325476, 0xc3d2e1f0 ]; + this.W = new Array(80); +} + +utils.inherits(SHA1, BlockHash); +module.exports = SHA1; + +SHA1.blockSize = 512; +SHA1.outSize = 160; +SHA1.hmacStrength = 80; +SHA1.padLength = 64; + +SHA1.prototype._update = function _update(msg, start) { + var W = this.W; + + for (var i = 0; i < 16; i++) + W[i] = msg[start + i]; + + for(; i < W.length; i++) + W[i] = rotl32(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); + + var a = this.h[0]; + var b = this.h[1]; + var c = this.h[2]; + var d = this.h[3]; + var e = this.h[4]; + + for (i = 0; i < W.length; i++) { + var s = ~~(i / 20); + var t = sum32_5(rotl32(a, 5), ft_1(s, b, c, d), e, W[i], sha1_K[s]); + e = d; + d = c; + c = rotl32(b, 30); + b = a; + a = t; + } + + this.h[0] = sum32(this.h[0], a); + this.h[1] = sum32(this.h[1], b); + this.h[2] = sum32(this.h[2], c); + this.h[3] = sum32(this.h[3], d); + this.h[4] = sum32(this.h[4], e); +}; + +SHA1.prototype._digest = function digest(enc) { + if (enc === 'hex') + return utils.toHex32(this.h, 'big'); + else + return utils.split32(this.h, 'big'); +}; + +},{"../common":129,"../utils":139,"./common":138}],134:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var SHA256 = require('./256'); + +function SHA224() { + if (!(this instanceof SHA224)) + return new SHA224(); + + SHA256.call(this); + this.h = [ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 ]; +} +utils.inherits(SHA224, SHA256); +module.exports = SHA224; + +SHA224.blockSize = 512; +SHA224.outSize = 224; +SHA224.hmacStrength = 192; +SHA224.padLength = 64; + +SHA224.prototype._digest = function digest(enc) { + // Just truncate output + if (enc === 'hex') + return utils.toHex32(this.h.slice(0, 7), 'big'); + else + return utils.split32(this.h.slice(0, 7), 'big'); +}; + + +},{"../utils":139,"./256":135}],135:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var common = require('../common'); +var shaCommon = require('./common'); +var assert = require('minimalistic-assert'); + +var sum32 = utils.sum32; +var sum32_4 = utils.sum32_4; +var sum32_5 = utils.sum32_5; +var ch32 = shaCommon.ch32; +var maj32 = shaCommon.maj32; +var s0_256 = shaCommon.s0_256; +var s1_256 = shaCommon.s1_256; +var g0_256 = shaCommon.g0_256; +var g1_256 = shaCommon.g1_256; + +var BlockHash = common.BlockHash; + +var sha256_K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +]; + +function SHA256() { + if (!(this instanceof SHA256)) + return new SHA256(); + + BlockHash.call(this); + this.h = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + ]; + this.k = sha256_K; + this.W = new Array(64); +} +utils.inherits(SHA256, BlockHash); +module.exports = SHA256; + +SHA256.blockSize = 512; +SHA256.outSize = 256; +SHA256.hmacStrength = 192; +SHA256.padLength = 64; + +SHA256.prototype._update = function _update(msg, start) { + var W = this.W; + + for (var i = 0; i < 16; i++) + W[i] = msg[start + i]; + for (; i < W.length; i++) + W[i] = sum32_4(g1_256(W[i - 2]), W[i - 7], g0_256(W[i - 15]), W[i - 16]); + + var a = this.h[0]; + var b = this.h[1]; + var c = this.h[2]; + var d = this.h[3]; + var e = this.h[4]; + var f = this.h[5]; + var g = this.h[6]; + var h = this.h[7]; + + assert(this.k.length === W.length); + for (i = 0; i < W.length; i++) { + var T1 = sum32_5(h, s1_256(e), ch32(e, f, g), this.k[i], W[i]); + var T2 = sum32(s0_256(a), maj32(a, b, c)); + h = g; + g = f; + f = e; + e = sum32(d, T1); + d = c; + c = b; + b = a; + a = sum32(T1, T2); + } + + this.h[0] = sum32(this.h[0], a); + this.h[1] = sum32(this.h[1], b); + this.h[2] = sum32(this.h[2], c); + this.h[3] = sum32(this.h[3], d); + this.h[4] = sum32(this.h[4], e); + this.h[5] = sum32(this.h[5], f); + this.h[6] = sum32(this.h[6], g); + this.h[7] = sum32(this.h[7], h); +}; + +SHA256.prototype._digest = function digest(enc) { + if (enc === 'hex') + return utils.toHex32(this.h, 'big'); + else + return utils.split32(this.h, 'big'); +}; + +},{"../common":129,"../utils":139,"./common":138,"minimalistic-assert":163}],136:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); + +var SHA512 = require('./512'); + +function SHA384() { + if (!(this instanceof SHA384)) + return new SHA384(); + + SHA512.call(this); + this.h = [ + 0xcbbb9d5d, 0xc1059ed8, + 0x629a292a, 0x367cd507, + 0x9159015a, 0x3070dd17, + 0x152fecd8, 0xf70e5939, + 0x67332667, 0xffc00b31, + 0x8eb44a87, 0x68581511, + 0xdb0c2e0d, 0x64f98fa7, + 0x47b5481d, 0xbefa4fa4 ]; +} +utils.inherits(SHA384, SHA512); +module.exports = SHA384; + +SHA384.blockSize = 1024; +SHA384.outSize = 384; +SHA384.hmacStrength = 192; +SHA384.padLength = 128; + +SHA384.prototype._digest = function digest(enc) { + if (enc === 'hex') + return utils.toHex32(this.h.slice(0, 12), 'big'); + else + return utils.split32(this.h.slice(0, 12), 'big'); +}; + +},{"../utils":139,"./512":137}],137:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var common = require('../common'); +var assert = require('minimalistic-assert'); + +var rotr64_hi = utils.rotr64_hi; +var rotr64_lo = utils.rotr64_lo; +var shr64_hi = utils.shr64_hi; +var shr64_lo = utils.shr64_lo; +var sum64 = utils.sum64; +var sum64_hi = utils.sum64_hi; +var sum64_lo = utils.sum64_lo; +var sum64_4_hi = utils.sum64_4_hi; +var sum64_4_lo = utils.sum64_4_lo; +var sum64_5_hi = utils.sum64_5_hi; +var sum64_5_lo = utils.sum64_5_lo; + +var BlockHash = common.BlockHash; + +var sha512_K = [ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, + 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, + 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, + 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, + 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, + 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, + 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, + 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, + 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, + 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, + 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, + 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, + 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, + 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, + 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, + 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, + 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, + 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, + 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, + 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, + 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, + 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, + 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, + 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, + 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, + 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, + 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, + 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, + 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, + 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, + 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, + 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, + 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, + 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, + 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, + 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 +]; + +function SHA512() { + if (!(this instanceof SHA512)) + return new SHA512(); + + BlockHash.call(this); + this.h = [ + 0x6a09e667, 0xf3bcc908, + 0xbb67ae85, 0x84caa73b, + 0x3c6ef372, 0xfe94f82b, + 0xa54ff53a, 0x5f1d36f1, + 0x510e527f, 0xade682d1, + 0x9b05688c, 0x2b3e6c1f, + 0x1f83d9ab, 0xfb41bd6b, + 0x5be0cd19, 0x137e2179 ]; + this.k = sha512_K; + this.W = new Array(160); +} +utils.inherits(SHA512, BlockHash); +module.exports = SHA512; + +SHA512.blockSize = 1024; +SHA512.outSize = 512; +SHA512.hmacStrength = 192; +SHA512.padLength = 128; + +SHA512.prototype._prepareBlock = function _prepareBlock(msg, start) { + var W = this.W; + + // 32 x 32bit words + for (var i = 0; i < 32; i++) + W[i] = msg[start + i]; + for (; i < W.length; i += 2) { + var c0_hi = g1_512_hi(W[i - 4], W[i - 3]); // i - 2 + var c0_lo = g1_512_lo(W[i - 4], W[i - 3]); + var c1_hi = W[i - 14]; // i - 7 + var c1_lo = W[i - 13]; + var c2_hi = g0_512_hi(W[i - 30], W[i - 29]); // i - 15 + var c2_lo = g0_512_lo(W[i - 30], W[i - 29]); + var c3_hi = W[i - 32]; // i - 16 + var c3_lo = W[i - 31]; + + W[i] = sum64_4_hi( + c0_hi, c0_lo, + c1_hi, c1_lo, + c2_hi, c2_lo, + c3_hi, c3_lo); + W[i + 1] = sum64_4_lo( + c0_hi, c0_lo, + c1_hi, c1_lo, + c2_hi, c2_lo, + c3_hi, c3_lo); + } +}; + +SHA512.prototype._update = function _update(msg, start) { + this._prepareBlock(msg, start); + + var W = this.W; + + var ah = this.h[0]; + var al = this.h[1]; + var bh = this.h[2]; + var bl = this.h[3]; + var ch = this.h[4]; + var cl = this.h[5]; + var dh = this.h[6]; + var dl = this.h[7]; + var eh = this.h[8]; + var el = this.h[9]; + var fh = this.h[10]; + var fl = this.h[11]; + var gh = this.h[12]; + var gl = this.h[13]; + var hh = this.h[14]; + var hl = this.h[15]; + + assert(this.k.length === W.length); + for (var i = 0; i < W.length; i += 2) { + var c0_hi = hh; + var c0_lo = hl; + var c1_hi = s1_512_hi(eh, el); + var c1_lo = s1_512_lo(eh, el); + var c2_hi = ch64_hi(eh, el, fh, fl, gh, gl); + var c2_lo = ch64_lo(eh, el, fh, fl, gh, gl); + var c3_hi = this.k[i]; + var c3_lo = this.k[i + 1]; + var c4_hi = W[i]; + var c4_lo = W[i + 1]; + + var T1_hi = sum64_5_hi( + c0_hi, c0_lo, + c1_hi, c1_lo, + c2_hi, c2_lo, + c3_hi, c3_lo, + c4_hi, c4_lo); + var T1_lo = sum64_5_lo( + c0_hi, c0_lo, + c1_hi, c1_lo, + c2_hi, c2_lo, + c3_hi, c3_lo, + c4_hi, c4_lo); + + c0_hi = s0_512_hi(ah, al); + c0_lo = s0_512_lo(ah, al); + c1_hi = maj64_hi(ah, al, bh, bl, ch, cl); + c1_lo = maj64_lo(ah, al, bh, bl, ch, cl); + + var T2_hi = sum64_hi(c0_hi, c0_lo, c1_hi, c1_lo); + var T2_lo = sum64_lo(c0_hi, c0_lo, c1_hi, c1_lo); + + hh = gh; + hl = gl; + + gh = fh; + gl = fl; + + fh = eh; + fl = el; + + eh = sum64_hi(dh, dl, T1_hi, T1_lo); + el = sum64_lo(dl, dl, T1_hi, T1_lo); + + dh = ch; + dl = cl; + + ch = bh; + cl = bl; + + bh = ah; + bl = al; + + ah = sum64_hi(T1_hi, T1_lo, T2_hi, T2_lo); + al = sum64_lo(T1_hi, T1_lo, T2_hi, T2_lo); + } + + sum64(this.h, 0, ah, al); + sum64(this.h, 2, bh, bl); + sum64(this.h, 4, ch, cl); + sum64(this.h, 6, dh, dl); + sum64(this.h, 8, eh, el); + sum64(this.h, 10, fh, fl); + sum64(this.h, 12, gh, gl); + sum64(this.h, 14, hh, hl); +}; + +SHA512.prototype._digest = function digest(enc) { + if (enc === 'hex') + return utils.toHex32(this.h, 'big'); + else + return utils.split32(this.h, 'big'); +}; + +function ch64_hi(xh, xl, yh, yl, zh) { + var r = (xh & yh) ^ ((~xh) & zh); + if (r < 0) + r += 0x100000000; + return r; +} + +function ch64_lo(xh, xl, yh, yl, zh, zl) { + var r = (xl & yl) ^ ((~xl) & zl); + if (r < 0) + r += 0x100000000; + return r; +} + +function maj64_hi(xh, xl, yh, yl, zh) { + var r = (xh & yh) ^ (xh & zh) ^ (yh & zh); + if (r < 0) + r += 0x100000000; + return r; +} + +function maj64_lo(xh, xl, yh, yl, zh, zl) { + var r = (xl & yl) ^ (xl & zl) ^ (yl & zl); + if (r < 0) + r += 0x100000000; + return r; +} + +function s0_512_hi(xh, xl) { + var c0_hi = rotr64_hi(xh, xl, 28); + var c1_hi = rotr64_hi(xl, xh, 2); // 34 + var c2_hi = rotr64_hi(xl, xh, 7); // 39 + + var r = c0_hi ^ c1_hi ^ c2_hi; + if (r < 0) + r += 0x100000000; + return r; +} + +function s0_512_lo(xh, xl) { + var c0_lo = rotr64_lo(xh, xl, 28); + var c1_lo = rotr64_lo(xl, xh, 2); // 34 + var c2_lo = rotr64_lo(xl, xh, 7); // 39 + + var r = c0_lo ^ c1_lo ^ c2_lo; + if (r < 0) + r += 0x100000000; + return r; +} + +function s1_512_hi(xh, xl) { + var c0_hi = rotr64_hi(xh, xl, 14); + var c1_hi = rotr64_hi(xh, xl, 18); + var c2_hi = rotr64_hi(xl, xh, 9); // 41 + + var r = c0_hi ^ c1_hi ^ c2_hi; + if (r < 0) + r += 0x100000000; + return r; +} + +function s1_512_lo(xh, xl) { + var c0_lo = rotr64_lo(xh, xl, 14); + var c1_lo = rotr64_lo(xh, xl, 18); + var c2_lo = rotr64_lo(xl, xh, 9); // 41 + + var r = c0_lo ^ c1_lo ^ c2_lo; + if (r < 0) + r += 0x100000000; + return r; +} + +function g0_512_hi(xh, xl) { + var c0_hi = rotr64_hi(xh, xl, 1); + var c1_hi = rotr64_hi(xh, xl, 8); + var c2_hi = shr64_hi(xh, xl, 7); + + var r = c0_hi ^ c1_hi ^ c2_hi; + if (r < 0) + r += 0x100000000; + return r; +} + +function g0_512_lo(xh, xl) { + var c0_lo = rotr64_lo(xh, xl, 1); + var c1_lo = rotr64_lo(xh, xl, 8); + var c2_lo = shr64_lo(xh, xl, 7); + + var r = c0_lo ^ c1_lo ^ c2_lo; + if (r < 0) + r += 0x100000000; + return r; +} + +function g1_512_hi(xh, xl) { + var c0_hi = rotr64_hi(xh, xl, 19); + var c1_hi = rotr64_hi(xl, xh, 29); // 61 + var c2_hi = shr64_hi(xh, xl, 6); + + var r = c0_hi ^ c1_hi ^ c2_hi; + if (r < 0) + r += 0x100000000; + return r; +} + +function g1_512_lo(xh, xl) { + var c0_lo = rotr64_lo(xh, xl, 19); + var c1_lo = rotr64_lo(xl, xh, 29); // 61 + var c2_lo = shr64_lo(xh, xl, 6); + + var r = c0_lo ^ c1_lo ^ c2_lo; + if (r < 0) + r += 0x100000000; + return r; +} + +},{"../common":129,"../utils":139,"minimalistic-assert":163}],138:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var rotr32 = utils.rotr32; + +function ft_1(s, x, y, z) { + if (s === 0) + return ch32(x, y, z); + if (s === 1 || s === 3) + return p32(x, y, z); + if (s === 2) + return maj32(x, y, z); +} +exports.ft_1 = ft_1; + +function ch32(x, y, z) { + return (x & y) ^ ((~x) & z); +} +exports.ch32 = ch32; + +function maj32(x, y, z) { + return (x & y) ^ (x & z) ^ (y & z); +} +exports.maj32 = maj32; + +function p32(x, y, z) { + return x ^ y ^ z; +} +exports.p32 = p32; + +function s0_256(x) { + return rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22); +} +exports.s0_256 = s0_256; + +function s1_256(x) { + return rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25); +} +exports.s1_256 = s1_256; + +function g0_256(x) { + return rotr32(x, 7) ^ rotr32(x, 18) ^ (x >>> 3); +} +exports.g0_256 = g0_256; + +function g1_256(x) { + return rotr32(x, 17) ^ rotr32(x, 19) ^ (x >>> 10); +} +exports.g1_256 = g1_256; + +},{"../utils":139}],139:[function(require,module,exports){ +'use strict'; + +var assert = require('minimalistic-assert'); +var inherits = require('inherits'); + +exports.inherits = inherits; + +function isSurrogatePair(msg, i) { + if ((msg.charCodeAt(i) & 0xFC00) !== 0xD800) { + return false; + } + if (i < 0 || i + 1 >= msg.length) { + return false; + } + return (msg.charCodeAt(i + 1) & 0xFC00) === 0xDC00; +} + +function toArray(msg, enc) { + if (Array.isArray(msg)) + return msg.slice(); + if (!msg) + return []; + var res = []; + if (typeof msg === 'string') { + if (!enc) { + // Inspired by stringToUtf8ByteArray() in closure-library by Google + // https://github.com/google/closure-library/blob/8598d87242af59aac233270742c8984e2b2bdbe0/closure/goog/crypt/crypt.js#L117-L143 + // Apache License 2.0 + // https://github.com/google/closure-library/blob/master/LICENSE + var p = 0; + for (var i = 0; i < msg.length; i++) { + var c = msg.charCodeAt(i); + if (c < 128) { + res[p++] = c; + } else if (c < 2048) { + res[p++] = (c >> 6) | 192; + res[p++] = (c & 63) | 128; + } else if (isSurrogatePair(msg, i)) { + c = 0x10000 + ((c & 0x03FF) << 10) + (msg.charCodeAt(++i) & 0x03FF); + res[p++] = (c >> 18) | 240; + res[p++] = ((c >> 12) & 63) | 128; + res[p++] = ((c >> 6) & 63) | 128; + res[p++] = (c & 63) | 128; + } else { + res[p++] = (c >> 12) | 224; + res[p++] = ((c >> 6) & 63) | 128; + res[p++] = (c & 63) | 128; + } + } + } else if (enc === 'hex') { + msg = msg.replace(/[^a-z0-9]+/ig, ''); + if (msg.length % 2 !== 0) + msg = '0' + msg; + for (i = 0; i < msg.length; i += 2) + res.push(parseInt(msg[i] + msg[i + 1], 16)); + } + } else { + for (i = 0; i < msg.length; i++) + res[i] = msg[i] | 0; + } + return res; +} +exports.toArray = toArray; + +function toHex(msg) { + var res = ''; + for (var i = 0; i < msg.length; i++) + res += zero2(msg[i].toString(16)); + return res; +} +exports.toHex = toHex; + +function htonl(w) { + var res = (w >>> 24) | + ((w >>> 8) & 0xff00) | + ((w << 8) & 0xff0000) | + ((w & 0xff) << 24); + return res >>> 0; +} +exports.htonl = htonl; + +function toHex32(msg, endian) { + var res = ''; + for (var i = 0; i < msg.length; i++) { + var w = msg[i]; + if (endian === 'little') + w = htonl(w); + res += zero8(w.toString(16)); + } + return res; +} +exports.toHex32 = toHex32; + +function zero2(word) { + if (word.length === 1) + return '0' + word; + else + return word; +} +exports.zero2 = zero2; + +function zero8(word) { + if (word.length === 7) + return '0' + word; + else if (word.length === 6) + return '00' + word; + else if (word.length === 5) + return '000' + word; + else if (word.length === 4) + return '0000' + word; + else if (word.length === 3) + return '00000' + word; + else if (word.length === 2) + return '000000' + word; + else if (word.length === 1) + return '0000000' + word; + else + return word; +} +exports.zero8 = zero8; + +function join32(msg, start, end, endian) { + var len = end - start; + assert(len % 4 === 0); + var res = new Array(len / 4); + for (var i = 0, k = start; i < res.length; i++, k += 4) { + var w; + if (endian === 'big') + w = (msg[k] << 24) | (msg[k + 1] << 16) | (msg[k + 2] << 8) | msg[k + 3]; + else + w = (msg[k + 3] << 24) | (msg[k + 2] << 16) | (msg[k + 1] << 8) | msg[k]; + res[i] = w >>> 0; + } + return res; +} +exports.join32 = join32; + +function split32(msg, endian) { + var res = new Array(msg.length * 4); + for (var i = 0, k = 0; i < msg.length; i++, k += 4) { + var m = msg[i]; + if (endian === 'big') { + res[k] = m >>> 24; + res[k + 1] = (m >>> 16) & 0xff; + res[k + 2] = (m >>> 8) & 0xff; + res[k + 3] = m & 0xff; + } else { + res[k + 3] = m >>> 24; + res[k + 2] = (m >>> 16) & 0xff; + res[k + 1] = (m >>> 8) & 0xff; + res[k] = m & 0xff; + } + } + return res; +} +exports.split32 = split32; + +function rotr32(w, b) { + return (w >>> b) | (w << (32 - b)); +} +exports.rotr32 = rotr32; + +function rotl32(w, b) { + return (w << b) | (w >>> (32 - b)); +} +exports.rotl32 = rotl32; + +function sum32(a, b) { + return (a + b) >>> 0; +} +exports.sum32 = sum32; + +function sum32_3(a, b, c) { + return (a + b + c) >>> 0; +} +exports.sum32_3 = sum32_3; + +function sum32_4(a, b, c, d) { + return (a + b + c + d) >>> 0; +} +exports.sum32_4 = sum32_4; + +function sum32_5(a, b, c, d, e) { + return (a + b + c + d + e) >>> 0; +} +exports.sum32_5 = sum32_5; + +function sum64(buf, pos, ah, al) { + var bh = buf[pos]; + var bl = buf[pos + 1]; + + var lo = (al + bl) >>> 0; + var hi = (lo < al ? 1 : 0) + ah + bh; + buf[pos] = hi >>> 0; + buf[pos + 1] = lo; +} +exports.sum64 = sum64; + +function sum64_hi(ah, al, bh, bl) { + var lo = (al + bl) >>> 0; + var hi = (lo < al ? 1 : 0) + ah + bh; + return hi >>> 0; +} +exports.sum64_hi = sum64_hi; + +function sum64_lo(ah, al, bh, bl) { + var lo = al + bl; + return lo >>> 0; +} +exports.sum64_lo = sum64_lo; + +function sum64_4_hi(ah, al, bh, bl, ch, cl, dh, dl) { + var carry = 0; + var lo = al; + lo = (lo + bl) >>> 0; + carry += lo < al ? 1 : 0; + lo = (lo + cl) >>> 0; + carry += lo < cl ? 1 : 0; + lo = (lo + dl) >>> 0; + carry += lo < dl ? 1 : 0; + + var hi = ah + bh + ch + dh + carry; + return hi >>> 0; +} +exports.sum64_4_hi = sum64_4_hi; + +function sum64_4_lo(ah, al, bh, bl, ch, cl, dh, dl) { + var lo = al + bl + cl + dl; + return lo >>> 0; +} +exports.sum64_4_lo = sum64_4_lo; + +function sum64_5_hi(ah, al, bh, bl, ch, cl, dh, dl, eh, el) { + var carry = 0; + var lo = al; + lo = (lo + bl) >>> 0; + carry += lo < al ? 1 : 0; + lo = (lo + cl) >>> 0; + carry += lo < cl ? 1 : 0; + lo = (lo + dl) >>> 0; + carry += lo < dl ? 1 : 0; + lo = (lo + el) >>> 0; + carry += lo < el ? 1 : 0; + + var hi = ah + bh + ch + dh + eh + carry; + return hi >>> 0; +} +exports.sum64_5_hi = sum64_5_hi; + +function sum64_5_lo(ah, al, bh, bl, ch, cl, dh, dl, eh, el) { + var lo = al + bl + cl + dl + el; + + return lo >>> 0; +} +exports.sum64_5_lo = sum64_5_lo; + +function rotr64_hi(ah, al, num) { + var r = (al << (32 - num)) | (ah >>> num); + return r >>> 0; +} +exports.rotr64_hi = rotr64_hi; + +function rotr64_lo(ah, al, num) { + var r = (ah << (32 - num)) | (al >>> num); + return r >>> 0; +} +exports.rotr64_lo = rotr64_lo; + +function shr64_hi(ah, al, num) { + return ah >>> num; +} +exports.shr64_hi = shr64_hi; + +function shr64_lo(ah, al, num) { + var r = (ah << (32 - num)) | (al >>> num); + return r >>> 0; +} +exports.shr64_lo = shr64_lo; + +},{"inherits":141,"minimalistic-assert":163}],140:[function(require,module,exports){ +'use strict'; + +var hash = require('hash.js'); +var utils = require('minimalistic-crypto-utils'); +var assert = require('minimalistic-assert'); + +function HmacDRBG(options) { + if (!(this instanceof HmacDRBG)) + return new HmacDRBG(options); + this.hash = options.hash; + this.predResist = !!options.predResist; + + this.outLen = this.hash.outSize; + this.minEntropy = options.minEntropy || this.hash.hmacStrength; + + this._reseed = null; + this.reseedInterval = null; + this.K = null; + this.V = null; + + var entropy = utils.toArray(options.entropy, options.entropyEnc || 'hex'); + var nonce = utils.toArray(options.nonce, options.nonceEnc || 'hex'); + var pers = utils.toArray(options.pers, options.persEnc || 'hex'); + assert(entropy.length >= (this.minEntropy / 8), + 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); + this._init(entropy, nonce, pers); +} +module.exports = HmacDRBG; + +HmacDRBG.prototype._init = function init(entropy, nonce, pers) { + var seed = entropy.concat(nonce).concat(pers); + + this.K = new Array(this.outLen / 8); + this.V = new Array(this.outLen / 8); + for (var i = 0; i < this.V.length; i++) { + this.K[i] = 0x00; + this.V[i] = 0x01; + } + + this._update(seed); + this._reseed = 1; + this.reseedInterval = 0x1000000000000; // 2^48 +}; + +HmacDRBG.prototype._hmac = function hmac() { + return new hash.hmac(this.hash, this.K); +}; + +HmacDRBG.prototype._update = function update(seed) { + var kmac = this._hmac() + .update(this.V) + .update([ 0x00 ]); + if (seed) + kmac = kmac.update(seed); + this.K = kmac.digest(); + this.V = this._hmac().update(this.V).digest(); + if (!seed) + return; + + this.K = this._hmac() + .update(this.V) + .update([ 0x01 ]) + .update(seed) + .digest(); + this.V = this._hmac().update(this.V).digest(); +}; + +HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) { + // Optional entropy enc + if (typeof entropyEnc !== 'string') { + addEnc = add; + add = entropyEnc; + entropyEnc = null; + } + + entropy = utils.toArray(entropy, entropyEnc); + add = utils.toArray(add, addEnc); + + assert(entropy.length >= (this.minEntropy / 8), + 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits'); + + this._update(entropy.concat(add || [])); + this._reseed = 1; +}; + +HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) { + if (this._reseed > this.reseedInterval) + throw new Error('Reseed is required'); + + // Optional encoding + if (typeof enc !== 'string') { + addEnc = add; + add = enc; + enc = null; + } + + // Optional additional data + if (add) { + add = utils.toArray(add, addEnc || 'hex'); + this._update(add); + } + + var temp = []; + while (temp.length < len) { + this.V = this._hmac().update(this.V).digest(); + temp = temp.concat(this.V); + } + + var res = temp.slice(0, len); + this._update(add); + this._reseed++; + return utils.encode(res, enc); +}; + +},{"hash.js":128,"minimalistic-assert":163,"minimalistic-crypto-utils":164}],141:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }) + } + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + if (superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } + } +} + +},{}],142:[function(require,module,exports){ +/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */ + +module.exports = function isBuffer (obj) { + return obj != null && obj.constructor != null && + typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} + +},{}],143:[function(require,module,exports){ +'use strict' + +const encodings = require('./lib/encodings') +const rangeOptions = new Set(['lt', 'gt', 'lte', 'gte']) + +module.exports = Codec + +function Codec (opts) { + if (!(this instanceof Codec)) { + return new Codec(opts) + } + this.opts = opts || {} + this.encodings = encodings +} + +Codec.prototype._encoding = function (encoding) { + if (typeof encoding === 'string') encoding = encodings[encoding] + if (!encoding) encoding = encodings.id + return encoding +} + +Codec.prototype._keyEncoding = function (opts, batchOpts) { + return this._encoding((batchOpts && batchOpts.keyEncoding) || + (opts && opts.keyEncoding) || + this.opts.keyEncoding) +} + +Codec.prototype._valueEncoding = function (opts, batchOpts) { + return this._encoding((batchOpts && (batchOpts.valueEncoding || batchOpts.encoding)) || + (opts && (opts.valueEncoding || opts.encoding)) || + (this.opts.valueEncoding || this.opts.encoding)) +} + +Codec.prototype.encodeKey = function (key, opts, batchOpts) { + return this._keyEncoding(opts, batchOpts).encode(key) +} + +Codec.prototype.encodeValue = function (value, opts, batchOpts) { + return this._valueEncoding(opts, batchOpts).encode(value) +} + +Codec.prototype.decodeKey = function (key, opts) { + return this._keyEncoding(opts).decode(key) +} + +Codec.prototype.decodeValue = function (value, opts) { + return this._valueEncoding(opts).decode(value) +} + +Codec.prototype.encodeBatch = function (ops, opts) { + return ops.map((_op) => { + const op = { + type: _op.type, + key: this.encodeKey(_op.key, opts, _op) + } + if (this.keyAsBuffer(opts, _op)) op.keyEncoding = 'binary' + if (_op.prefix) op.prefix = _op.prefix + if ('value' in _op) { + op.value = this.encodeValue(_op.value, opts, _op) + if (this.valueAsBuffer(opts, _op)) op.valueEncoding = 'binary' + } + return op + }) +} + +Codec.prototype.encodeLtgt = function (ltgt) { + const ret = {} + + for (const key of Object.keys(ltgt)) { + if (key === 'start' || key === 'end') { + throw new Error('Legacy range options ("start" and "end") have been removed') + } + + ret[key] = rangeOptions.has(key) + ? this.encodeKey(ltgt[key], ltgt) + : ltgt[key] + } + + return ret +} + +Codec.prototype.createStreamDecoder = function (opts) { + if (opts.keys && opts.values) { + return (key, value) => { + return { + key: this.decodeKey(key, opts), + value: this.decodeValue(value, opts) + } + } + } else if (opts.keys) { + return (key) => { + return this.decodeKey(key, opts) + } + } else if (opts.values) { + return (_, value) => { + return this.decodeValue(value, opts) + } + } else { + return function () {} + } +} + +Codec.prototype.keyAsBuffer = function (opts) { + return this._keyEncoding(opts).buffer +} + +Codec.prototype.valueAsBuffer = function (opts) { + return this._valueEncoding(opts).buffer +} + +},{"./lib/encodings":144}],144:[function(require,module,exports){ +'use strict' + +const { Buffer } = require('buffer') + +exports.utf8 = exports['utf-8'] = { + encode: function (data) { + return isBinary(data) ? data : String(data) + }, + decode: identity, + buffer: false, + type: 'utf8' +} + +exports.json = { + encode: JSON.stringify, + decode: JSON.parse, + buffer: false, + type: 'json' +} + +exports.binary = { + encode: function (data) { + return isBinary(data) ? data : Buffer.from(data) + }, + decode: identity, + buffer: true, + type: 'binary' +} + +exports.none = { + encode: identity, + decode: identity, + buffer: false, + type: 'id' +} + +exports.id = exports.none + +const bufferEncodings = [ + 'hex', + 'ascii', + 'base64', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le' +] + +for (const type of bufferEncodings) { + exports[type] = { + encode: function (data) { + return isBinary(data) ? data : Buffer.from(data, type) + }, + decode: function (buffer) { + return buffer.toString(type) + }, + buffer: true, + type: type + } +} + +function identity (value) { + return value +} + +function isBinary (data) { + return data === undefined || data === null || Buffer.isBuffer(data) +} + +},{"buffer":336}],145:[function(require,module,exports){ +'use strict' + +function createError (type, Proto) { + const Err = function (message, cause) { + if (typeof message === 'object' && message !== null) { + // Can be passed just a cause + cause = cause || message + message = message.message || message.name + } + + message = message || '' + cause = cause || undefined + + // If input is already of type, return as-is to keep its stack trace. + // Avoid instanceof, for when node_modules has multiple copies of level-errors. + if (typeof cause === 'object' && cause.type === type && cause.message === message) { + return cause + } + + Object.defineProperty(this, 'type', { value: type, enumerable: false, writable: true, configurable: true }) + Object.defineProperty(this, 'name', { value: type, enumerable: false, writable: true, configurable: true }) + Object.defineProperty(this, 'cause', { value: cause, enumerable: false, writable: true, configurable: true }) + Object.defineProperty(this, 'message', { value: message, enumerable: false, writable: true, configurable: true }) + + Error.call(this) + + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, Err) + } + } + + Err.prototype = new Proto() + return Err +} + +const LevelUPError = createError('LevelUPError', Error) + +module.exports = { + LevelUPError: LevelUPError, + InitializationError: createError('InitializationError', LevelUPError), + OpenError: createError('OpenError', LevelUPError), + ReadError: createError('ReadError', LevelUPError), + WriteError: createError('WriteError', LevelUPError), + NotFoundError: createError('NotFoundError', LevelUPError), + EncodingError: createError('EncodingError', LevelUPError) +} + +module.exports.NotFoundError.prototype.notFound = true +module.exports.NotFoundError.prototype.status = 404 + +},{}],146:[function(require,module,exports){ +'use strict' + +const inherits = require('inherits') +const { Readable } = require('readable-stream') + +module.exports = ReadStream +inherits(ReadStream, Readable) + +function ReadStream (iterator, options) { + if (!(this instanceof ReadStream)) return new ReadStream(iterator, options) + options = options || {} + Readable.call(this, Object.assign({}, options, { + objectMode: true + })) + this._iterator = iterator + this._options = options + this.on('end', this.destroy.bind(this, null, null)) +} + +ReadStream.prototype._read = function () { + if (this.destroyed) return + + this._iterator.next((err, key, value) => { + if (this.destroyed) return + if (err) return this.destroy(err) + + if (key === undefined && value === undefined) { + this.push(null) + } else if (this._options.keys !== false && this._options.values === false) { + this.push(key) + } else if (this._options.keys === false && this._options.values !== false) { + this.push(value) + } else { + this.push({ key, value }) + } + }) +} + +ReadStream.prototype._destroy = function (err, callback) { + this._iterator.end(function (err2) { + callback(err || err2) + }) +} + +},{"inherits":141,"readable-stream":197}],147:[function(require,module,exports){ +/* global indexedDB */ + +'use strict' + +module.exports = Level + +const AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN +const inherits = require('inherits') +const parallel = require('run-parallel-limit') +const Iterator = require('./iterator') +const serialize = require('./util/serialize') +const deserialize = require('./util/deserialize') +const support = require('./util/support') +const clear = require('./util/clear') +const createKeyRange = require('./util/key-range') + +const DEFAULT_PREFIX = 'level-js-' + +function Level (location, opts) { + if (!(this instanceof Level)) return new Level(location, opts) + + AbstractLevelDOWN.call(this, { + bufferKeys: support.bufferKeys(indexedDB), + snapshots: true, + permanence: true, + clear: true, + getMany: true + }) + + opts = opts || {} + + if (typeof location !== 'string') { + throw new Error('constructor requires a location string argument') + } + + this.location = location + this.prefix = opts.prefix == null ? DEFAULT_PREFIX : opts.prefix + this.version = parseInt(opts.version || 1, 10) +} + +inherits(Level, AbstractLevelDOWN) + +Level.prototype.type = 'level-js' + +Level.prototype._open = function (options, callback) { + const req = indexedDB.open(this.prefix + this.location, this.version) + + req.onerror = function () { + callback(req.error || new Error('unknown error')) + } + + req.onsuccess = () => { + this.db = req.result + callback() + } + + req.onupgradeneeded = (ev) => { + const db = ev.target.result + + if (!db.objectStoreNames.contains(this.location)) { + db.createObjectStore(this.location) + } + } +} + +Level.prototype.store = function (mode) { + const transaction = this.db.transaction([this.location], mode) + return transaction.objectStore(this.location) +} + +Level.prototype.await = function (request, callback) { + const transaction = request.transaction + + // Take advantage of the fact that a non-canceled request error aborts + // the transaction. I.e. no need to listen for "request.onerror". + transaction.onabort = function () { + callback(transaction.error || new Error('aborted by user')) + } + + transaction.oncomplete = function () { + callback(null, request.result) + } +} + +Level.prototype._get = function (key, options, callback) { + const store = this.store('readonly') + let req + + try { + req = store.get(key) + } catch (err) { + return this._nextTick(callback, err) + } + + this.await(req, function (err, value) { + if (err) return callback(err) + + if (value === undefined) { + // 'NotFound' error, consistent with LevelDOWN API + return callback(new Error('NotFound')) + } + + callback(null, deserialize(value, options.asBuffer)) + }) +} + +Level.prototype._getMany = function (keys, options, callback) { + const asBuffer = options.asBuffer + const store = this.store('readonly') + const tasks = keys.map((key) => (next) => { + let request + + try { + request = store.get(key) + } catch (err) { + return next(err) + } + + request.onsuccess = () => { + const value = request.result + next(null, value === undefined ? value : deserialize(value, asBuffer)) + } + + request.onerror = (ev) => { + ev.stopPropagation() + next(request.error) + } + }) + + parallel(tasks, 16, callback) +} + +Level.prototype._del = function (key, options, callback) { + const store = this.store('readwrite') + let req + + try { + req = store.delete(key) + } catch (err) { + return this._nextTick(callback, err) + } + + this.await(req, callback) +} + +Level.prototype._put = function (key, value, options, callback) { + const store = this.store('readwrite') + let req + + try { + // Will throw a DataError or DataCloneError if the environment + // does not support serializing the key or value respectively. + req = store.put(value, key) + } catch (err) { + return this._nextTick(callback, err) + } + + this.await(req, callback) +} + +Level.prototype._serializeKey = function (key) { + return serialize(key, this.supports.bufferKeys) +} + +Level.prototype._serializeValue = function (value) { + return serialize(value, true) +} + +Level.prototype._iterator = function (options) { + return new Iterator(this, this.location, options) +} + +Level.prototype._batch = function (operations, options, callback) { + if (operations.length === 0) return this._nextTick(callback) + + const store = this.store('readwrite') + const transaction = store.transaction + let index = 0 + let error + + transaction.onabort = function () { + callback(error || transaction.error || new Error('aborted by user')) + } + + transaction.oncomplete = function () { + callback() + } + + // Wait for a request to complete before making the next, saving CPU. + function loop () { + const op = operations[index++] + const key = op.key + + let req + + try { + req = op.type === 'del' ? store.delete(key) : store.put(op.value, key) + } catch (err) { + error = err + transaction.abort() + return + } + + if (index < operations.length) { + req.onsuccess = loop + } + } + + loop() +} + +Level.prototype._clear = function (options, callback) { + let keyRange + let req + + try { + keyRange = createKeyRange(options) + } catch (e) { + // The lower key is greater than the upper key. + // IndexedDB throws an error, but we'll just do nothing. + return this._nextTick(callback) + } + + if (options.limit >= 0) { + // IDBObjectStore#delete(range) doesn't have such an option. + // Fall back to cursor-based implementation. + return clear(this, this.location, keyRange, options, callback) + } + + try { + const store = this.store('readwrite') + req = keyRange ? store.delete(keyRange) : store.clear() + } catch (err) { + return this._nextTick(callback, err) + } + + this.await(req, callback) +} + +Level.prototype._close = function (callback) { + this.db.close() + this._nextTick(callback) +} + +// NOTE: remove in a next major release +Level.prototype.upgrade = function (callback) { + if (this.status !== 'open') { + return this._nextTick(callback, new Error('cannot upgrade() before open()')) + } + + const it = this.iterator() + const batchOptions = {} + const self = this + + it._deserializeKey = it._deserializeValue = identity + next() + + function next (err) { + if (err) return finish(err) + it.next(each) + } + + function each (err, key, value) { + if (err || key === undefined) { + return finish(err) + } + + const newKey = self._serializeKey(deserialize(key, true)) + const newValue = self._serializeValue(deserialize(value, true)) + + // To bypass serialization on the old key, use _batch() instead of batch(). + // NOTE: if we disable snapshotting (#86) this could lead to a loop of + // inserting and then iterating those same entries, because the new keys + // possibly sort after the old keys. + self._batch([ + { type: 'del', key: key }, + { type: 'put', key: newKey, value: newValue } + ], batchOptions, next) + } + + function finish (err) { + it.end(function (err2) { + callback(err || err2) + }) + } + + function identity (data) { + return data + } +} + +Level.destroy = function (location, prefix, callback) { + if (typeof prefix === 'function') { + callback = prefix + prefix = DEFAULT_PREFIX + } + const request = indexedDB.deleteDatabase(prefix + location) + request.onsuccess = function () { + callback() + } + request.onerror = function (err) { + callback(err) + } +} + +},{"./iterator":148,"./util/clear":149,"./util/deserialize":150,"./util/key-range":151,"./util/serialize":152,"./util/support":153,"abstract-leveldown":80,"inherits":141,"run-parallel-limit":198}],148:[function(require,module,exports){ +'use strict' + +const inherits = require('inherits') +const AbstractIterator = require('abstract-leveldown').AbstractIterator +const createKeyRange = require('./util/key-range') +const deserialize = require('./util/deserialize') +const noop = function () {} + +module.exports = Iterator + +function Iterator (db, location, options) { + AbstractIterator.call(this, db) + + this._limit = options.limit + this._count = 0 + this._callback = null + this._cache = [] + this._completed = false + this._aborted = false + this._error = null + this._transaction = null + + this._keys = options.keys + this._values = options.values + this._keyAsBuffer = options.keyAsBuffer + this._valueAsBuffer = options.valueAsBuffer + + if (this._limit === 0) { + this._completed = true + return + } + + let keyRange + + try { + keyRange = createKeyRange(options) + } catch (e) { + // The lower key is greater than the upper key. + // IndexedDB throws an error, but we'll just return 0 results. + this._completed = true + return + } + + this.createIterator(location, keyRange, options.reverse) +} + +inherits(Iterator, AbstractIterator) + +Iterator.prototype.createIterator = function (location, keyRange, reverse) { + const transaction = this.db.db.transaction([location], 'readonly') + const store = transaction.objectStore(location) + const req = store.openCursor(keyRange, reverse ? 'prev' : 'next') + + req.onsuccess = (ev) => { + const cursor = ev.target.result + if (cursor) this.onItem(cursor) + } + + this._transaction = transaction + + // If an error occurs (on the request), the transaction will abort. + transaction.onabort = () => { + this.onAbort(this._transaction.error || new Error('aborted by user')) + } + + transaction.oncomplete = () => { + this.onComplete() + } +} + +Iterator.prototype.onItem = function (cursor) { + this._cache.push(cursor.key, cursor.value) + + if (this._limit <= 0 || ++this._count < this._limit) { + cursor.continue() + } + + this.maybeNext() +} + +Iterator.prototype.onAbort = function (err) { + this._aborted = true + this._error = err + this.maybeNext() +} + +Iterator.prototype.onComplete = function () { + this._completed = true + this.maybeNext() +} + +Iterator.prototype.maybeNext = function () { + if (this._callback) { + this._next(this._callback) + this._callback = null + } +} + +Iterator.prototype._next = function (callback) { + if (this._aborted) { + // The error should be picked up by either next() or end(). + const err = this._error + this._error = null + this._nextTick(callback, err) + } else if (this._cache.length > 0) { + let key = this._cache.shift() + let value = this._cache.shift() + + if (this._keys && key !== undefined) { + key = this._deserializeKey(key, this._keyAsBuffer) + } else { + key = undefined + } + + if (this._values && value !== undefined) { + value = this._deserializeValue(value, this._valueAsBuffer) + } else { + value = undefined + } + + this._nextTick(callback, null, key, value) + } else if (this._completed) { + this._nextTick(callback) + } else { + this._callback = callback + } +} + +// Exposed for the v4 to v5 upgrade utility +Iterator.prototype._deserializeKey = deserialize +Iterator.prototype._deserializeValue = deserialize + +Iterator.prototype._end = function (callback) { + if (this._aborted || this._completed) { + return this._nextTick(callback, this._error) + } + + // Don't advance the cursor anymore, and the transaction will complete + // on its own in the next tick. This approach is much cleaner than calling + // transaction.abort() with its unpredictable event order. + this.onItem = noop + this.onAbort = callback + this.onComplete = callback +} + +},{"./util/deserialize":150,"./util/key-range":151,"abstract-leveldown":80,"inherits":141}],149:[function(require,module,exports){ +'use strict' + +module.exports = function clear (db, location, keyRange, options, callback) { + if (options.limit === 0) return db._nextTick(callback) + + const transaction = db.db.transaction([location], 'readwrite') + const store = transaction.objectStore(location) + let count = 0 + + transaction.oncomplete = function () { + callback() + } + + transaction.onabort = function () { + callback(transaction.error || new Error('aborted by user')) + } + + // A key cursor is faster (skips reading values) but not supported by IE + const method = store.openKeyCursor ? 'openKeyCursor' : 'openCursor' + const direction = options.reverse ? 'prev' : 'next' + + store[method](keyRange, direction).onsuccess = function (ev) { + const cursor = ev.target.result + + if (cursor) { + // Wait for a request to complete before continuing, saving CPU. + store.delete(cursor.key).onsuccess = function () { + if (options.limit <= 0 || ++count < options.limit) { + cursor.continue() + } + } + } + } +} + +},{}],150:[function(require,module,exports){ +(function (global){(function (){ +'use strict' + +const Buffer = require('buffer').Buffer +const ta2str = (function () { + if (global.TextDecoder) { + const decoder = new TextDecoder('utf-8') + return decoder.decode.bind(decoder) + } else { + return function ta2str (ta) { + return ta2buf(ta).toString() + } + } +})() + +const ab2str = (function () { + if (global.TextDecoder) { + const decoder = new TextDecoder('utf-8') + return decoder.decode.bind(decoder) + } else { + return function ab2str (ab) { + return Buffer.from(ab).toString() + } + } +})() + +function ta2buf (ta) { + const buf = Buffer.from(ta.buffer) + + if (ta.byteLength === ta.buffer.byteLength) { + return buf + } else { + return buf.slice(ta.byteOffset, ta.byteOffset + ta.byteLength) + } +} + +module.exports = function (data, asBuffer) { + if (data instanceof Uint8Array) { + return asBuffer ? ta2buf(data) : ta2str(data) + } else if (data instanceof ArrayBuffer) { + return asBuffer ? Buffer.from(data) : ab2str(data) + } else { + return asBuffer ? Buffer.from(String(data)) : String(data) + } +} + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"buffer":336}],151:[function(require,module,exports){ +/* global IDBKeyRange */ + +'use strict' + +const ltgt = require('ltgt') +const NONE = Symbol('none') + +module.exports = function createKeyRange (options) { + const lower = ltgt.lowerBound(options, NONE) + const upper = ltgt.upperBound(options, NONE) + const lowerOpen = ltgt.lowerBoundExclusive(options, NONE) + const upperOpen = ltgt.upperBoundExclusive(options, NONE) + + if (lower !== NONE && upper !== NONE) { + return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen) + } else if (lower !== NONE) { + return IDBKeyRange.lowerBound(lower, lowerOpen) + } else if (upper !== NONE) { + return IDBKeyRange.upperBound(upper, upperOpen) + } else { + return null + } +} + +},{"ltgt":162}],152:[function(require,module,exports){ +(function (global){(function (){ +'use strict' + +const Buffer = require('buffer').Buffer +// Returns either a Uint8Array or Buffer (doesn't matter to +// IndexedDB, because Buffer is a subclass of Uint8Array) +const str2bin = (function () { + if (global.TextEncoder) { + const encoder = new TextEncoder('utf-8') + return encoder.encode.bind(encoder) + } else { + return Buffer.from + } +})() + +module.exports = function (data, asBuffer) { + if (asBuffer) { + return Buffer.isBuffer(data) ? data : str2bin(String(data)) + } else { + return String(data) + } +} + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"buffer":336}],153:[function(require,module,exports){ +'use strict' + +const Buffer = require('buffer').Buffer + +exports.test = function (key) { + return function test (impl) { + try { + impl.cmp(key, 0) + return true + } catch (err) { + return false + } + } +} + +// Detect binary key support (IndexedDB Second Edition) +exports.bufferKeys = exports.test(Buffer.alloc(0)) + +},{"buffer":336}],154:[function(require,module,exports){ +'use strict' + +const levelup = require('levelup') +const encode = require('encoding-down') + +function packager (leveldown) { + function Level (location, options, callback) { + if (typeof location === 'function') { + callback = location + } else if (typeof options === 'function') { + callback = options + } + + if (!isObject(options)) { + options = isObject(location) ? location : {} + } + + return levelup(encode(leveldown(location, options), options), options, callback) + } + + function isObject (o) { + return typeof o === 'object' && o !== null + } + + for (const m of ['destroy', 'repair']) { + if (typeof leveldown[m] === 'function') { + Level[m] = function (...args) { + leveldown[m](...args) + } + } + } + + Level.errors = levelup.errors + + return Level +} + +module.exports = packager + +},{"encoding-down":126,"levelup":159}],155:[function(require,module,exports){ +'use strict' + +module.exports = function supports (...manifests) { + const manifest = manifests.reduce((acc, m) => Object.assign(acc, m), {}) + + return Object.assign(manifest, { + // Features of abstract-leveldown + bufferKeys: manifest.bufferKeys || false, + snapshots: manifest.snapshots || false, + permanence: manifest.permanence || false, + seek: manifest.seek || false, + clear: manifest.clear || false, + getMany: manifest.getMany || false, + keyIterator: manifest.keyIterator || false, + valueIterator: manifest.valueIterator || false, + iteratorNextv: manifest.iteratorNextv || false, + iteratorAll: manifest.iteratorAll || false, + + // Features of abstract-leveldown that levelup doesn't have + status: manifest.status || false, + idempotentOpen: manifest.idempotentOpen || false, + passiveOpen: manifest.passiveOpen || false, + serialize: manifest.serialize || false, + + // Features of disk-based implementations + createIfMissing: manifest.createIfMissing || false, + errorIfExists: manifest.errorIfExists || false, + + // Features of level(up) that abstract-leveldown doesn't have yet + deferredOpen: manifest.deferredOpen || false, + openCallback: manifest.openCallback || false, + promises: manifest.promises || false, + streams: manifest.streams || false, + encodings: manifest.encodings || false, + events: maybeObject(manifest.events), + + // Methods that are not part of abstract-leveldown or levelup + additionalMethods: Object.assign({}, manifest.additionalMethods) + }) +} + +function maybeObject (value) { + return !value ? false : Object.assign({}, value) +} + +},{}],156:[function(require,module,exports){ +module.exports = require('level-packager')(require('level-js')) + +},{"level-js":147,"level-packager":154}],157:[function(require,module,exports){ +'use strict' + +const WriteError = require('level-errors').WriteError +const catering = require('catering') +const getCallback = require('./common').getCallback +const getOptions = require('./common').getOptions + +function Batch (levelup) { + this.db = levelup + this.batch = levelup.db.batch() + this.ops = [] + this.length = 0 +} + +Batch.prototype.put = function (key, value, options) { + try { + this.batch.put(key, value, options) + } catch (e) { + throw new WriteError(e) + } + + this.ops.push({ ...options, type: 'put', key, value }) + this.length++ + + return this +} + +Batch.prototype.del = function (key, options) { + try { + this.batch.del(key, options) + } catch (err) { + throw new WriteError(err) + } + + this.ops.push({ ...options, type: 'del', key }) + this.length++ + + return this +} + +Batch.prototype.clear = function () { + try { + this.batch.clear() + } catch (err) { + throw new WriteError(err) + } + + this.ops = [] + this.length = 0 + + return this +} + +Batch.prototype.write = function (options, callback) { + const levelup = this.db + const ops = this.ops + + callback = getCallback(options, callback) + callback = catering.fromCallback(callback) + options = getOptions(options) + + try { + this.batch.write(options, function (err) { + if (err) { return callback(new WriteError(err)) } + levelup.emit('batch', ops) + callback() + }) + } catch (err) { + throw new WriteError(err) + } + + return callback.promise +} + +module.exports = Batch + +},{"./common":158,"catering":104,"level-errors":145}],158:[function(require,module,exports){ +arguments[4][81][0].apply(exports,arguments) +},{"dup":81}],159:[function(require,module,exports){ +'use strict' + +const EventEmitter = require('events').EventEmitter +const inherits = require('util').inherits +const DeferredLevelDOWN = require('deferred-leveldown') +const IteratorStream = require('level-iterator-stream') +const Batch = require('./batch') +const errors = require('level-errors') +const supports = require('level-supports') +const catering = require('catering') +const getCallback = require('./common').getCallback +const getOptions = require('./common').getOptions + +// TODO: after we drop node 10, also use queueMicrotask() in node +const nextTick = require('./next-tick') + +const WriteError = errors.WriteError +const ReadError = errors.ReadError +const NotFoundError = errors.NotFoundError +const OpenError = errors.OpenError +const InitializationError = errors.InitializationError + +function LevelUP (db, options, callback) { + if (!(this instanceof LevelUP)) { + return new LevelUP(db, options, callback) + } + + let error + + EventEmitter.call(this) + this.setMaxListeners(Infinity) + + if (typeof options === 'function') { + callback = options + options = {} + } + + options = options || {} + + if (!db || typeof db !== 'object') { + error = new InitializationError('First argument must be an abstract-leveldown compliant store') + if (typeof callback === 'function') { + return nextTick(callback, error) + } + throw error + } + + if (typeof db.status !== 'string') { + throw new Error('.status required, old abstract-leveldown') + } + + this.options = getOptions(options) + this._db = db + this.db = null + this.open(callback || ((err) => { + if (err) this.emit('error', err) + })) + + // Create manifest based on deferred-leveldown's + this.supports = supports(this.db.supports, { + status: true, + deferredOpen: true, + openCallback: true, + promises: true, + streams: true + }) + + // Experimental: enrich levelup interface + for (const method of Object.keys(this.supports.additionalMethods)) { + if (this[method] != null) continue + + // Don't do this.db[method].bind() because this.db is dynamic. + this[method] = function (...args) { + return this.db[method](...args) + } + } +} + +LevelUP.prototype.emit = EventEmitter.prototype.emit +LevelUP.prototype.once = EventEmitter.prototype.once +inherits(LevelUP, EventEmitter) + +// TODO: tests +Object.defineProperty(LevelUP.prototype, 'status', { + enumerable: true, + get () { + return this.db.status + } +}) + +// TODO: tests +LevelUP.prototype.isOperational = function () { + return this.db.status === 'open' || this.db.status === 'opening' +} + +LevelUP.prototype.open = function (opts, callback) { + if (typeof opts === 'function') { + callback = opts + opts = null + } + + callback = catering.fromCallback(callback) + + if (!opts) { + opts = this.options + } + + // 1) Don't check db.status until levelup has opened, + // in order for levelup events to be consistent + if (this.db && this.isOpen()) { + nextTick(callback, null, this) + return callback.promise + } + + if (this.db && this._isOpening()) { + this.once('open', () => { callback(null, this) }) + return callback.promise + } + + // 2) Instead let deferred-leveldown handle already-open cases. + // TODO: ideally though, levelup would have its own status + this.db = new DeferredLevelDOWN(this._db) + this.emit('opening') + + this.db.open(opts, (err) => { + if (err) { + return callback(new OpenError(err)) + } + this.db = this._db + callback(null, this) + this.emit('open') + this.emit('ready') + }) + + return callback.promise +} + +LevelUP.prototype.close = function (callback) { + callback = catering.fromCallback(callback) + + if (this.isOpen()) { + this.db.close((err, ...rest) => { + this.emit('closed') + callback(err, ...rest) + }) + this.emit('closing') + } else if (this.isClosed()) { + nextTick(callback) + } else if (this.db.status === 'closing') { + this.once('closed', callback) + } else if (this._isOpening()) { + this.once('open', () => { + this.close(callback) + }) + } + + return callback.promise +} + +// TODO: remove in future major +LevelUP.prototype.isOpen = function () { + return this.db.status === 'open' +} + +// TODO: remove in future major +LevelUP.prototype._isOpening = function () { + return this.db.status === 'opening' +} + +// TODO: remove in future major +LevelUP.prototype.isClosed = function () { + return (/^clos|new/).test(this.db.status) +} + +LevelUP.prototype.get = function (key, options, callback) { + callback = getCallback(options, callback) + callback = catering.fromCallback(callback) + + if (maybeError(this, callback)) { + return callback.promise + } + + options = getOptions(options) + + this.db.get(key, options, function (err, value) { + if (err) { + if ((/notfound/i).test(err) || err.notFound) { + err = new NotFoundError('Key not found in database [' + key + ']', err) + } else { + err = new ReadError(err) + } + return callback(err) + } + callback(null, value) + }) + + return callback.promise +} + +LevelUP.prototype.getMany = function (keys, options, callback) { + return this.db.getMany(keys, options, callback) +} + +LevelUP.prototype.put = function (key, value, options, callback) { + callback = getCallback(options, callback) + callback = catering.fromCallback(callback) + + if (maybeError(this, callback)) { + return callback.promise + } + + options = getOptions(options) + + this.db.put(key, value, options, (err) => { + if (err) { + return callback(new WriteError(err)) + } + this.emit('put', key, value) + callback() + }) + + return callback.promise +} + +LevelUP.prototype.del = function (key, options, callback) { + callback = getCallback(options, callback) + callback = catering.fromCallback(callback) + + if (maybeError(this, callback)) { + return callback.promise + } + + options = getOptions(options) + + this.db.del(key, options, (err) => { + if (err) { + return callback(new WriteError(err)) + } + this.emit('del', key) + callback() + }) + + return callback.promise +} + +LevelUP.prototype.batch = function (arr, options, callback) { + if (!arguments.length) { + return new Batch(this) + } + + if (typeof arr === 'function') callback = arr + else callback = getCallback(options, callback) + + callback = catering.fromCallback(callback) + + if (maybeError(this, callback)) { + return callback.promise + } + + options = getOptions(options) + + this.db.batch(arr, options, (err) => { + if (err) { + return callback(new WriteError(err)) + } + this.emit('batch', arr) + callback() + }) + + return callback.promise +} + +LevelUP.prototype.iterator = function (options) { + return this.db.iterator(options) +} + +LevelUP.prototype.clear = function (options, callback) { + callback = getCallback(options, callback) + options = getOptions(options) + callback = catering.fromCallback(callback) + + if (maybeError(this, callback)) { + return callback.promise + } + + this.db.clear(options, (err) => { + if (err) { + return callback(new WriteError(err)) + } + this.emit('clear', options) + callback() + }) + + return callback.promise +} + +LevelUP.prototype.readStream = +LevelUP.prototype.createReadStream = function (options) { + options = Object.assign({ keys: true, values: true }, options) + if (typeof options.limit !== 'number') { options.limit = -1 } + return new IteratorStream(this.db.iterator(options), options) +} + +LevelUP.prototype.keyStream = +LevelUP.prototype.createKeyStream = function (options) { + return this.createReadStream(Object.assign({}, options, { keys: true, values: false })) +} + +LevelUP.prototype.valueStream = +LevelUP.prototype.createValueStream = function (options) { + return this.createReadStream(Object.assign({}, options, { keys: false, values: true })) +} + +LevelUP.prototype.toString = function () { + return 'LevelUP' +} + +LevelUP.prototype.type = 'levelup' + +// Expose nextTick for API parity with abstract-leveldown +LevelUP.prototype._nextTick = nextTick + +function maybeError (db, callback) { + if (!db.isOperational()) { + nextTick(callback, new ReadError('Database is not open')) + return true + } + + return false +} + +LevelUP.errors = errors +module.exports = LevelUP + +},{"./batch":157,"./common":158,"./next-tick":160,"catering":104,"deferred-leveldown":107,"events":395,"level-errors":145,"level-iterator-stream":146,"level-supports":155,"util":354}],160:[function(require,module,exports){ +arguments[4][82][0].apply(exports,arguments) +},{"dup":82,"queue-microtask":181}],161:[function(require,module,exports){ +(function (global){(function (){ +/** + * @license + * Lodash + * Copyright OpenJS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ +;(function() { + + /** Used as a safe reference for `undefined` in pre-ES5 environments. */ + var undefined; + + /** Used as the semantic version number. */ + var VERSION = '4.17.21'; + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Error message constants. */ + var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.', + FUNC_ERROR_TEXT = 'Expected a function', + INVALID_TEMPL_VAR_ERROR_TEXT = 'Invalid `variable` option passed into `_.template`'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used as the maximum memoize cache size. */ + var MAX_MEMOIZE_SIZE = 500; + + /** Used as the internal argument placeholder. */ + var PLACEHOLDER = '__lodash_placeholder__'; + + /** Used to compose bitmasks for cloning. */ + var CLONE_DEEP_FLAG = 1, + CLONE_FLAT_FLAG = 2, + CLONE_SYMBOLS_FLAG = 4; + + /** Used to compose bitmasks for value comparisons. */ + var COMPARE_PARTIAL_FLAG = 1, + COMPARE_UNORDERED_FLAG = 2; + + /** Used to compose bitmasks for function metadata. */ + var WRAP_BIND_FLAG = 1, + WRAP_BIND_KEY_FLAG = 2, + WRAP_CURRY_BOUND_FLAG = 4, + WRAP_CURRY_FLAG = 8, + WRAP_CURRY_RIGHT_FLAG = 16, + WRAP_PARTIAL_FLAG = 32, + WRAP_PARTIAL_RIGHT_FLAG = 64, + WRAP_ARY_FLAG = 128, + WRAP_REARG_FLAG = 256, + WRAP_FLIP_FLAG = 512; + + /** Used as default options for `_.truncate`. */ + var DEFAULT_TRUNC_LENGTH = 30, + DEFAULT_TRUNC_OMISSION = '...'; + + /** Used to detect hot functions by number of calls within a span of milliseconds. */ + var HOT_COUNT = 800, + HOT_SPAN = 16; + + /** Used to indicate the type of lazy iteratees. */ + var LAZY_FILTER_FLAG = 1, + LAZY_MAP_FLAG = 2, + LAZY_WHILE_FLAG = 3; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + + /** Used as references for the maximum length and index of an array. */ + var MAX_ARRAY_LENGTH = 4294967295, + MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, + HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; + + /** Used to associate wrap methods with their bit flags. */ + var wrapFlags = [ + ['ary', WRAP_ARY_FLAG], + ['bind', WRAP_BIND_FLAG], + ['bindKey', WRAP_BIND_KEY_FLAG], + ['curry', WRAP_CURRY_FLAG], + ['curryRight', WRAP_CURRY_RIGHT_FLAG], + ['flip', WRAP_FLIP_FLAG], + ['partial', WRAP_PARTIAL_FLAG], + ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], + ['rearg', WRAP_REARG_FLAG] + ]; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + asyncTag = '[object AsyncFunction]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + domExcTag = '[object DOMException]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + nullTag = '[object Null]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + proxyTag = '[object Proxy]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + undefinedTag = '[object Undefined]', + weakMapTag = '[object WeakMap]', + weakSetTag = '[object WeakSet]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match empty string literals in compiled template source. */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to match HTML entities and HTML characters. */ + var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g, + reUnescapedHtml = /[&<>"']/g, + reHasEscapedHtml = RegExp(reEscapedHtml.source), + reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** Used to match template delimiters. */ + var reEscape = /<%-([\s\S]+?)%>/g, + reEvaluate = /<%([\s\S]+?)%>/g, + reInterpolate = /<%=([\s\S]+?)%>/g; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g, + reHasRegExpChar = RegExp(reRegExpChar.source); + + /** Used to match leading whitespace. */ + var reTrimStart = /^\s+/; + + /** Used to match a single whitespace character. */ + var reWhitespace = /\s/; + + /** Used to match wrap detail comments. */ + var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, + reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/, + reSplitDetails = /,? & /; + + /** Used to match words composed of alphanumeric characters. */ + var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; + + /** + * Used to validate the `validate` option in `_.template` variable. + * + * Forbids characters which could potentially change the meaning of the function argument definition: + * - "()," (modification of function parameters) + * - "=" (default value) + * - "[]{}" (destructuring of function parameters) + * - "/" (beginning of a comment) + * - whitespace + */ + var reForbiddenIdentifierChars = /[()=,{}\[\]\/\s]/; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** + * Used to match + * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components). + */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect bad signed hexadecimal string values. */ + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + + /** Used to detect binary string values. */ + var reIsBinary = /^0b[01]+$/i; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect octal string values. */ + var reIsOctal = /^0o[0-7]+$/i; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to match Latin Unicode letters (excluding mathematical operators). */ + var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; + + /** Used to ensure capturing order of template delimiters. */ + var reNoMatch = /($^)/; + + /** Used to match unescaped characters in compiled string literals. */ + var reUnescapedString = /['\n\r\u2028\u2029\\]/g; + + /** Used to compose unicode character classes. */ + var rsAstralRange = '\\ud800-\\udfff', + rsComboMarksRange = '\\u0300-\\u036f', + reComboHalfMarksRange = '\\ufe20-\\ufe2f', + rsComboSymbolsRange = '\\u20d0-\\u20ff', + rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, + rsDingbatRange = '\\u2700-\\u27bf', + rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', + rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', + rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', + rsPunctuationRange = '\\u2000-\\u206f', + rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', + rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', + rsVarRange = '\\ufe0e\\ufe0f', + rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; + + /** Used to compose unicode capture groups. */ + var rsApos = "['\u2019]", + rsAstral = '[' + rsAstralRange + ']', + rsBreak = '[' + rsBreakRange + ']', + rsCombo = '[' + rsComboRange + ']', + rsDigits = '\\d+', + rsDingbat = '[' + rsDingbatRange + ']', + rsLower = '[' + rsLowerRange + ']', + rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', + rsFitz = '\\ud83c[\\udffb-\\udfff]', + rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', + rsNonAstral = '[^' + rsAstralRange + ']', + rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', + rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', + rsUpper = '[' + rsUpperRange + ']', + rsZWJ = '\\u200d'; + + /** Used to compose unicode regexes. */ + var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')', + rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')', + rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', + rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', + reOptMod = rsModifier + '?', + rsOptVar = '[' + rsVarRange + ']?', + rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', + rsOrdLower = '\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])', + rsOrdUpper = '\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])', + rsSeq = rsOptVar + reOptMod + rsOptJoin, + rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq, + rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; + + /** Used to match apostrophes. */ + var reApos = RegExp(rsApos, 'g'); + + /** + * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and + * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). + */ + var reComboMark = RegExp(rsCombo, 'g'); + + /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ + var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); + + /** Used to match complex or compound words. */ + var reUnicodeWord = RegExp([ + rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', + rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', + rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, + rsUpper + '+' + rsOptContrUpper, + rsOrdUpper, + rsOrdLower, + rsDigits, + rsEmoji + ].join('|'), 'g'); + + /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ + var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); + + /** Used to detect strings that need a more robust regexp to match words. */ + var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; + + /** Used to assign default `context` object properties. */ + var contextProps = [ + 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', + 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', + 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', + 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', + '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout' + ]; + + /** Used to make template sourceURLs easier to identify. */ + var templateCounter = -1; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = + cloneableTags[boolTag] = cloneableTags[dateTag] = + cloneableTags[float32Tag] = cloneableTags[float64Tag] = + cloneableTags[int8Tag] = cloneableTags[int16Tag] = + cloneableTags[int32Tag] = cloneableTags[mapTag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[setTag] = + cloneableTags[stringTag] = cloneableTags[symbolTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[weakMapTag] = false; + + /** Used to map Latin Unicode letters to basic Latin letters. */ + var deburredLetters = { + // Latin-1 Supplement block. + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss', + // Latin Extended-A block. + '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', + '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', + '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', + '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', + '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', + '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', + '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', + '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', + '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', + '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', + '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', + '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', + '\u0134': 'J', '\u0135': 'j', + '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', + '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', + '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', + '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', + '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', + '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', + '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', + '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', + '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', + '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', + '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', + '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', + '\u0163': 't', '\u0165': 't', '\u0167': 't', + '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', + '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', + '\u0174': 'W', '\u0175': 'w', + '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', + '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', + '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', + '\u0132': 'IJ', '\u0133': 'ij', + '\u0152': 'Oe', '\u0153': 'oe', + '\u0149': "'n", '\u017f': 's' + }; + + /** Used to map characters to HTML entities. */ + var htmlEscapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + /** Used to map HTML entities to characters. */ + var htmlUnescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'" + }; + + /** Used to escape characters for inclusion in compiled string literals. */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /** Built-in method references without a dependency on `root`. */ + var freeParseFloat = parseFloat, + freeParseInt = parseInt; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Detect free variable `exports`. */ + var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports; + + /** Detect free variable `process` from Node.js. */ + var freeProcess = moduleExports && freeGlobal.process; + + /** Used to access faster Node.js helpers. */ + var nodeUtil = (function() { + try { + // Use `util.types` for Node.js 10+. + var types = freeModule && freeModule.require && freeModule.require('util').types; + + if (types) { + return types; + } + + // Legacy `process.binding('util')` for Node.js < 10. + return freeProcess && freeProcess.binding && freeProcess.binding('util'); + } catch (e) {} + }()); + + /* Node.js helper references. */ + var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer, + nodeIsDate = nodeUtil && nodeUtil.isDate, + nodeIsMap = nodeUtil && nodeUtil.isMap, + nodeIsRegExp = nodeUtil && nodeUtil.isRegExp, + nodeIsSet = nodeUtil && nodeUtil.isSet, + nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + + /*--------------------------------------------------------------------------*/ + + /** + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. + */ + function apply(func, thisArg, args) { + switch (args.length) { + case 0: return func.call(thisArg); + case 1: return func.call(thisArg, args[0]); + case 2: return func.call(thisArg, args[0], args[1]); + case 3: return func.call(thisArg, args[0], args[1], args[2]); + } + return func.apply(thisArg, args); + } + + /** + * A specialized version of `baseAggregator` for arrays. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function arrayAggregator(array, setter, iteratee, accumulator) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + var value = array[index]; + setter(accumulator, value, iteratee(value), array); + } + return accumulator; + } + + /** + * A specialized version of `_.forEach` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.forEachRight` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEachRight(array, iteratee) { + var length = array == null ? 0 : array.length; + + while (length--) { + if (iteratee(array[length], length, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.every` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + */ + function arrayEvery(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + + /** + * A specialized version of `_.filter` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function arrayFilter(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * A specialized version of `_.includes` for arrays without support for + * specifying an index to search from. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludes(array, value) { + var length = array == null ? 0 : array.length; + return !!length && baseIndexOf(array, value, 0) > -1; + } + + /** + * This function is like `arrayIncludes` except that it accepts a comparator. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @param {Function} comparator The comparator invoked per element. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludesWith(array, value, comparator) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (comparator(value, array[index])) { + return true; + } + } + return false; + } + + /** + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function arrayMap(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + /** + * A specialized version of `_.reduce` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the first element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduce(array, iteratee, accumulator, initAccum) { + var index = -1, + length = array == null ? 0 : array.length; + + if (initAccum && length) { + accumulator = array[++index]; + } + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); + } + return accumulator; + } + + /** + * A specialized version of `_.reduceRight` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the last element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduceRight(array, iteratee, accumulator, initAccum) { + var length = array == null ? 0 : array.length; + if (initAccum && length) { + accumulator = array[--length]; + } + while (length--) { + accumulator = iteratee(accumulator, array[length], length, array); + } + return accumulator; + } + + /** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * Gets the size of an ASCII `string`. + * + * @private + * @param {string} string The string inspect. + * @returns {number} Returns the string size. + */ + var asciiSize = baseProperty('length'); + + /** + * Converts an ASCII `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function asciiToArray(string) { + return string.split(''); + } + + /** + * Splits an ASCII `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function asciiWords(string) { + return string.match(reAsciiWord) || []; + } + + /** + * The base implementation of methods like `_.findKey` and `_.findLastKey`, + * without support for iteratee shorthands, which iterates over `collection` + * using `eachFunc`. + * + * @private + * @param {Array|Object} collection The collection to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the found element or its key, else `undefined`. + */ + function baseFindKey(collection, predicate, eachFunc) { + var result; + eachFunc(collection, function(value, key, collection) { + if (predicate(value, key, collection)) { + result = key; + return false; + } + }); + return result; + } + + /** + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseFindIndex(array, predicate, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 1 : -1); + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.indexOf` without `fromIndex` bounds checks. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOf(array, value, fromIndex) { + return value === value + ? strictIndexOf(array, value, fromIndex) + : baseFindIndex(array, baseIsNaN, fromIndex); + } + + /** + * This function is like `baseIndexOf` except that it accepts a comparator. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @param {Function} comparator The comparator invoked per element. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOfWith(array, value, fromIndex, comparator) { + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (comparator(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.isNaN` without support for number objects. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + */ + function baseIsNaN(value) { + return value !== value; + } + + /** + * The base implementation of `_.mean` and `_.meanBy` without support for + * iteratee shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the mean. + */ + function baseMean(array, iteratee) { + var length = array == null ? 0 : array.length; + return length ? (baseSum(array, iteratee) / length) : NAN; + } + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.propertyOf` without support for deep paths. + * + * @private + * @param {Object} object The object to query. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyOf(object) { + return function(key) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.reduce` and `_.reduceRight`, without support + * for iteratee shorthands, which iterates over `collection` using `eachFunc`. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} accumulator The initial value. + * @param {boolean} initAccum Specify using the first or last element of + * `collection` as the initial value. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the accumulated value. + */ + function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { + eachFunc(collection, function(value, index, collection) { + accumulator = initAccum + ? (initAccum = false, value) + : iteratee(accumulator, value, index, collection); + }); + return accumulator; + } + + /** + * The base implementation of `_.sortBy` which uses `comparer` to define the + * sort order of `array` and replaces criteria objects with their corresponding + * values. + * + * @private + * @param {Array} array The array to sort. + * @param {Function} comparer The function to define sort order. + * @returns {Array} Returns `array`. + */ + function baseSortBy(array, comparer) { + var length = array.length; + + array.sort(comparer); + while (length--) { + array[length] = array[length].value; + } + return array; + } + + /** + * The base implementation of `_.sum` and `_.sumBy` without support for + * iteratee shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the sum. + */ + function baseSum(array, iteratee) { + var result, + index = -1, + length = array.length; + + while (++index < length) { + var current = iteratee(array[index]); + if (current !== undefined) { + result = result === undefined ? current : (result + current); + } + } + return result; + } + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + + /** + * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array + * of key-value pairs for `object` corresponding to the property names of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the key-value pairs. + */ + function baseToPairs(object, props) { + return arrayMap(props, function(key) { + return [key, object[key]]; + }); + } + + /** + * The base implementation of `_.trim`. + * + * @private + * @param {string} string The string to trim. + * @returns {string} Returns the trimmed string. + */ + function baseTrim(string) { + return string + ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') + : string; + } + + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + + /** + * The base implementation of `_.values` and `_.valuesIn` which creates an + * array of `object` property values corresponding to the property names + * of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the array of property values. + */ + function baseValues(object, props) { + return arrayMap(props, function(key) { + return object[key]; + }); + } + + /** + * Checks if a `cache` value for `key` exists. + * + * @private + * @param {Object} cache The cache to query. + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function cacheHas(cache, key) { + return cache.has(key); + } + + /** + * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol + * that is not found in the character symbols. + * + * @private + * @param {Array} strSymbols The string symbols to inspect. + * @param {Array} chrSymbols The character symbols to find. + * @returns {number} Returns the index of the first unmatched string symbol. + */ + function charsStartIndex(strSymbols, chrSymbols) { + var index = -1, + length = strSymbols.length; + + while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} + return index; + } + + /** + * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol + * that is not found in the character symbols. + * + * @private + * @param {Array} strSymbols The string symbols to inspect. + * @param {Array} chrSymbols The character symbols to find. + * @returns {number} Returns the index of the last unmatched string symbol. + */ + function charsEndIndex(strSymbols, chrSymbols) { + var index = strSymbols.length; + + while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} + return index; + } + + /** + * Gets the number of `placeholder` occurrences in `array`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} placeholder The placeholder to search for. + * @returns {number} Returns the placeholder count. + */ + function countHolders(array, placeholder) { + var length = array.length, + result = 0; + + while (length--) { + if (array[length] === placeholder) { + ++result; + } + } + return result; + } + + /** + * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A + * letters to basic Latin letters. + * + * @private + * @param {string} letter The matched letter to deburr. + * @returns {string} Returns the deburred letter. + */ + var deburrLetter = basePropertyOf(deburredLetters); + + /** + * Used by `_.escape` to convert characters to HTML entities. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + var escapeHtmlChar = basePropertyOf(htmlEscapes); + + /** + * Used by `_.template` to escape characters for inclusion in compiled string literals. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + function escapeStringChar(chr) { + return '\\' + stringEscapes[chr]; + } + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `string` contains Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a symbol is found, else `false`. + */ + function hasUnicode(string) { + return reHasUnicode.test(string); + } + + /** + * Checks if `string` contains a word composed of Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a word is found, else `false`. + */ + function hasUnicodeWord(string) { + return reHasUnicodeWord.test(string); + } + + /** + * Converts `iterator` to an array. + * + * @private + * @param {Object} iterator The iterator to convert. + * @returns {Array} Returns the converted array. + */ + function iteratorToArray(iterator) { + var data, + result = []; + + while (!(data = iterator.next()).done) { + result.push(data.value); + } + return result; + } + + /** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ + function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** + * Replaces all `placeholder` elements in `array` with an internal placeholder + * and returns an array of their indexes. + * + * @private + * @param {Array} array The array to modify. + * @param {*} placeholder The placeholder to replace. + * @returns {Array} Returns the new array of placeholder indexes. + */ + function replaceHolders(array, placeholder) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value === placeholder || value === PLACEHOLDER) { + array[index] = PLACEHOLDER; + result[resIndex++] = index; + } + } + return result; + } + + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + + /** + * Converts `set` to its value-value pairs. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the value-value pairs. + */ + function setToPairs(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = [value, value]; + }); + return result; + } + + /** + * A specialized version of `_.indexOf` which performs strict equality + * comparisons of values, i.e. `===`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function strictIndexOf(array, value, fromIndex) { + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * A specialized version of `_.lastIndexOf` which performs strict equality + * comparisons of values, i.e. `===`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function strictLastIndexOf(array, value, fromIndex) { + var index = fromIndex + 1; + while (index--) { + if (array[index] === value) { + return index; + } + } + return index; + } + + /** + * Gets the number of symbols in `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the string size. + */ + function stringSize(string) { + return hasUnicode(string) + ? unicodeSize(string) + : asciiSize(string); + } + + /** + * Converts `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function stringToArray(string) { + return hasUnicode(string) + ? unicodeToArray(string) + : asciiToArray(string); + } + + /** + * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace + * character of `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the index of the last non-whitespace character. + */ + function trimmedEndIndex(string) { + var index = string.length; + + while (index-- && reWhitespace.test(string.charAt(index))) {} + return index; + } + + /** + * Used by `_.unescape` to convert HTML entities to characters. + * + * @private + * @param {string} chr The matched character to unescape. + * @returns {string} Returns the unescaped character. + */ + var unescapeHtmlChar = basePropertyOf(htmlUnescapes); + + /** + * Gets the size of a Unicode `string`. + * + * @private + * @param {string} string The string inspect. + * @returns {number} Returns the string size. + */ + function unicodeSize(string) { + var result = reUnicode.lastIndex = 0; + while (reUnicode.test(string)) { + ++result; + } + return result; + } + + /** + * Converts a Unicode `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function unicodeToArray(string) { + return string.match(reUnicode) || []; + } + + /** + * Splits a Unicode `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function unicodeWords(string) { + return string.match(reUnicodeWord) || []; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a new pristine `lodash` function using the `context` object. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Util + * @param {Object} [context=root] The context object. + * @returns {Function} Returns a new `lodash` function. + * @example + * + * _.mixin({ 'foo': _.constant('foo') }); + * + * var lodash = _.runInContext(); + * lodash.mixin({ 'bar': lodash.constant('bar') }); + * + * _.isFunction(_.foo); + * // => true + * _.isFunction(_.bar); + * // => false + * + * lodash.isFunction(lodash.foo); + * // => false + * lodash.isFunction(lodash.bar); + * // => true + * + * // Create a suped-up `defer` in Node.js. + * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; + */ + var runInContext = (function runInContext(context) { + context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps)); + + /** Built-in constructor references. */ + var Array = context.Array, + Date = context.Date, + Error = context.Error, + Function = context.Function, + Math = context.Math, + Object = context.Object, + RegExp = context.RegExp, + String = context.String, + TypeError = context.TypeError; + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = context['__core-js_shared__']; + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to generate unique IDs. */ + var idCounter = 0; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var nativeObjectToString = objectProto.toString; + + /** Used to infer the `Object` constructor. */ + var objectCtorString = funcToString.call(Object); + + /** Used to restore the original `_` reference in `_.noConflict`. */ + var oldDash = root._; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Buffer = moduleExports ? context.Buffer : undefined, + Symbol = context.Symbol, + Uint8Array = context.Uint8Array, + allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, + getPrototype = overArg(Object.getPrototypeOf, Object), + objectCreate = Object.create, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice, + spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined, + symIterator = Symbol ? Symbol.iterator : undefined, + symToStringTag = Symbol ? Symbol.toStringTag : undefined; + + var defineProperty = (function() { + try { + var func = getNative(Object, 'defineProperty'); + func({}, '', {}); + return func; + } catch (e) {} + }()); + + /** Mocked built-ins. */ + var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout, + ctxNow = Date && Date.now !== root.Date.now && Date.now, + ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeFloor = Math.floor, + nativeGetSymbols = Object.getOwnPropertySymbols, + nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, + nativeIsFinite = context.isFinite, + nativeJoin = arrayProto.join, + nativeKeys = overArg(Object.keys, Object), + nativeMax = Math.max, + nativeMin = Math.min, + nativeNow = Date.now, + nativeParseInt = context.parseInt, + nativeRandom = Math.random, + nativeReverse = arrayProto.reverse; + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(context, 'DataView'), + Map = getNative(context, 'Map'), + Promise = getNative(context, 'Promise'), + Set = getNative(context, 'Set'), + WeakMap = getNative(context, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to store function metadata. */ + var metaMap = WeakMap && new WeakMap; + + /** Used to lookup unminified function names. */ + var realNames = {}; + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps `value` to enable implicit method + * chain sequences. Methods that operate on and return arrays, collections, + * and functions can be chained together. Methods that retrieve a single value + * or may return a primitive value will automatically end the chain sequence + * and return the unwrapped value. Otherwise, the value must be unwrapped + * with `_#value`. + * + * Explicit chain sequences, which must be unwrapped with `_#value`, may be + * enabled using `_.chain`. + * + * The execution of chained methods is lazy, that is, it's deferred until + * `_#value` is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. + * Shortcut fusion is an optimization to merge iteratee calls; this avoids + * the creation of intermediate arrays and can greatly reduce the number of + * iteratee executions. Sections of a chain sequence qualify for shortcut + * fusion if the section is applied to an array and iteratees accept only + * one argument. The heuristic for whether a section qualifies for shortcut + * fusion is subject to change. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, + * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, + * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, + * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, + * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, + * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, + * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, + * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, + * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, + * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, + * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, + * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, + * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, + * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, + * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, + * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, + * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, + * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, + * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, + * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, + * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, + * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, + * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, + * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, + * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, + * `zipObject`, `zipObjectDeep`, and `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, + * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, + * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, + * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, + * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, + * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, + * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, + * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, + * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, + * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, + * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, + * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, + * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, + * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, + * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, + * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, + * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, + * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, + * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, + * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, + * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, + * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, + * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, + * `upperFirst`, `value`, and `words` + * + * @name _ + * @constructor + * @category Seq + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2, 3]); + * + * // Returns an unwrapped value. + * wrapped.reduce(_.add); + * // => 6 + * + * // Returns a wrapped value. + * var squares = wrapped.map(square); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash(value) { + if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { + if (value instanceof LodashWrapper) { + return value; + } + if (hasOwnProperty.call(value, '__wrapped__')) { + return wrapperClone(value); + } + } + return new LodashWrapper(value); + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} proto The object to inherit from. + * @returns {Object} Returns the new object. + */ + var baseCreate = (function() { + function object() {} + return function(proto) { + if (!isObject(proto)) { + return {}; + } + if (objectCreate) { + return objectCreate(proto); + } + object.prototype = proto; + var result = new object; + object.prototype = undefined; + return result; + }; + }()); + + /** + * The function whose prototype chain sequence wrappers inherit from. + * + * @private + */ + function baseLodash() { + // No operation performed. + } + + /** + * The base constructor for creating `lodash` wrapper objects. + * + * @private + * @param {*} value The value to wrap. + * @param {boolean} [chainAll] Enable explicit method chain sequences. + */ + function LodashWrapper(value, chainAll) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__chain__ = !!chainAll; + this.__index__ = 0; + this.__values__ = undefined; + } + + /** + * By default, the template delimiters used by lodash are like those in + * embedded Ruby (ERB) as well as ES2015 template strings. Change the + * following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type {Object} + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'escape': reEscape, + + /** + * Used to detect code to be evaluated. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'evaluate': reEvaluate, + + /** + * Used to detect `data` property values to inject. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'interpolate': reInterpolate, + + /** + * Used to reference the data object in the template text. + * + * @memberOf _.templateSettings + * @type {string} + */ + 'variable': '', + + /** + * Used to import variables into the compiled template. + * + * @memberOf _.templateSettings + * @type {Object} + */ + 'imports': { + + /** + * A reference to the `lodash` function. + * + * @memberOf _.templateSettings.imports + * @type {Function} + */ + '_': lodash + } + }; + + // Ensure wrappers are instances of `baseLodash`. + lodash.prototype = baseLodash.prototype; + lodash.prototype.constructor = lodash; + + LodashWrapper.prototype = baseCreate(baseLodash.prototype); + LodashWrapper.prototype.constructor = LodashWrapper; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. + * + * @private + * @constructor + * @param {*} value The value to wrap. + */ + function LazyWrapper(value) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__dir__ = 1; + this.__filtered__ = false; + this.__iteratees__ = []; + this.__takeCount__ = MAX_ARRAY_LENGTH; + this.__views__ = []; + } + + /** + * Creates a clone of the lazy wrapper object. + * + * @private + * @name clone + * @memberOf LazyWrapper + * @returns {Object} Returns the cloned `LazyWrapper` object. + */ + function lazyClone() { + var result = new LazyWrapper(this.__wrapped__); + result.__actions__ = copyArray(this.__actions__); + result.__dir__ = this.__dir__; + result.__filtered__ = this.__filtered__; + result.__iteratees__ = copyArray(this.__iteratees__); + result.__takeCount__ = this.__takeCount__; + result.__views__ = copyArray(this.__views__); + return result; + } + + /** + * Reverses the direction of lazy iteration. + * + * @private + * @name reverse + * @memberOf LazyWrapper + * @returns {Object} Returns the new reversed `LazyWrapper` object. + */ + function lazyReverse() { + if (this.__filtered__) { + var result = new LazyWrapper(this); + result.__dir__ = -1; + result.__filtered__ = true; + } else { + result = this.clone(); + result.__dir__ *= -1; + } + return result; + } + + /** + * Extracts the unwrapped value from its lazy wrapper. + * + * @private + * @name value + * @memberOf LazyWrapper + * @returns {*} Returns the unwrapped value. + */ + function lazyValue() { + var array = this.__wrapped__.value(), + dir = this.__dir__, + isArr = isArray(array), + isRight = dir < 0, + arrLength = isArr ? array.length : 0, + view = getView(0, arrLength, this.__views__), + start = view.start, + end = view.end, + length = end - start, + index = isRight ? end : (start - 1), + iteratees = this.__iteratees__, + iterLength = iteratees.length, + resIndex = 0, + takeCount = nativeMin(length, this.__takeCount__); + + if (!isArr || (!isRight && arrLength == length && takeCount == length)) { + return baseWrapperValue(array, this.__actions__); + } + var result = []; + + outer: + while (length-- && resIndex < takeCount) { + index += dir; + + var iterIndex = -1, + value = array[index]; + + while (++iterIndex < iterLength) { + var data = iteratees[iterIndex], + iteratee = data.iteratee, + type = data.type, + computed = iteratee(value); + + if (type == LAZY_MAP_FLAG) { + value = computed; + } else if (!computed) { + if (type == LAZY_FILTER_FLAG) { + continue outer; + } else { + break outer; + } + } + } + result[resIndex++] = value; + } + return result; + } + + // Ensure `LazyWrapper` is an instance of `baseLodash`. + LazyWrapper.prototype = baseCreate(baseLodash.prototype); + LazyWrapper.prototype.constructor = LazyWrapper; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + this.size = 0; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + var result = this.has(key) && delete this.__data__[key]; + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + this.size += this.has(key) ? 0 : 1; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + this.size = 0; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + --this.size; + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + ++this.size; + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.size = 0; + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + var result = getMapData(this, key)['delete'](key); + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + var data = getMapData(this, key), + size = data.size; + + data.set(key, value); + this.size += data.size == size ? 0 : 1; + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * + * Creates an array cache object to store unique values. + * + * @private + * @constructor + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var index = -1, + length = values == null ? 0 : values.length; + + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } + } + + /** + * Adds `value` to the array cache. + * + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. + */ + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; + } + + /** + * Checks if `value` is in the array cache. + * + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. + */ + function setCacheHas(value) { + return this.__data__.has(value); + } + + // Add methods to `SetCache`. + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Stack(entries) { + var data = this.__data__ = new ListCache(entries); + this.size = data.size; + } + + /** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ + function stackClear() { + this.__data__ = new ListCache; + this.size = 0; + } + + /** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function stackDelete(key) { + var data = this.__data__, + result = data['delete'](key); + + this.size = data.size; + return result; + } + + /** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function stackGet(key) { + return this.__data__.get(key); + } + + /** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function stackHas(key) { + return this.__data__.has(key); + } + + /** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ + function stackSet(key, value) { + var data = this.__data__; + if (data instanceof ListCache) { + var pairs = data.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + this.size = ++data.size; + return this; + } + data = this.__data__ = new MapCache(pairs); + } + data.set(key, value); + this.size = data.size; + return this; + } + + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ + function arrayLikeKeys(value, inherited) { + var isArr = isArray(value), + isArg = !isArr && isArguments(value), + isBuff = !isArr && !isArg && isBuffer(value), + isType = !isArr && !isArg && !isBuff && isTypedArray(value), + skipIndexes = isArr || isArg || isBuff || isType, + result = skipIndexes ? baseTimes(value.length, String) : [], + length = result.length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && ( + // Safari 9 has enumerable `arguments.length` in strict mode. + key == 'length' || + // Node.js 0.10 has enumerable non-index properties on buffers. + (isBuff && (key == 'offset' || key == 'parent')) || + // PhantomJS 2 has enumerable non-index properties on typed arrays. + (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || + // Skip index properties. + isIndex(key, length) + ))) { + result.push(key); + } + } + return result; + } + + /** + * A specialized version of `_.sample` for arrays. + * + * @private + * @param {Array} array The array to sample. + * @returns {*} Returns the random element. + */ + function arraySample(array) { + var length = array.length; + return length ? array[baseRandom(0, length - 1)] : undefined; + } + + /** + * A specialized version of `_.sampleSize` for arrays. + * + * @private + * @param {Array} array The array to sample. + * @param {number} n The number of elements to sample. + * @returns {Array} Returns the random elements. + */ + function arraySampleSize(array, n) { + return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length)); + } + + /** + * A specialized version of `_.shuffle` for arrays. + * + * @private + * @param {Array} array The array to shuffle. + * @returns {Array} Returns the new shuffled array. + */ + function arrayShuffle(array) { + return shuffleSelf(copyArray(array)); + } + + /** + * This function is like `assignValue` except that it doesn't assign + * `undefined` values. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignMergeValue(object, key, value) { + if ((value !== undefined && !eq(object[key], value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Assigns `value` to `key` of `object` if the existing value is not equivalent + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignValue(object, key, value) { + var objValue = object[key]; + if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * Aggregates elements of `collection` on `accumulator` with keys transformed + * by `iteratee` and values set by `setter`. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function baseAggregator(collection, setter, iteratee, accumulator) { + baseEach(collection, function(value, key, collection) { + setter(accumulator, value, iteratee(value), collection); + }); + return accumulator; + } + + /** + * The base implementation of `_.assign` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return object && copyObject(source, keys(source), object); + } + + /** + * The base implementation of `_.assignIn` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssignIn(object, source) { + return object && copyObject(source, keysIn(source), object); + } + + /** + * The base implementation of `assignValue` and `assignMergeValue` without + * value checks. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function baseAssignValue(object, key, value) { + if (key == '__proto__' && defineProperty) { + defineProperty(object, key, { + 'configurable': true, + 'enumerable': true, + 'value': value, + 'writable': true + }); + } else { + object[key] = value; + } + } + + /** + * The base implementation of `_.at` without support for individual paths. + * + * @private + * @param {Object} object The object to iterate over. + * @param {string[]} paths The property paths to pick. + * @returns {Array} Returns the picked elements. + */ + function baseAt(object, paths) { + var index = -1, + length = paths.length, + result = Array(length), + skip = object == null; + + while (++index < length) { + result[index] = skip ? undefined : get(object, paths[index]); + } + return result; + } + + /** + * The base implementation of `_.clamp` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + */ + function baseClamp(number, lower, upper) { + if (number === number) { + if (upper !== undefined) { + number = number <= upper ? number : upper; + } + if (lower !== undefined) { + number = number >= lower ? number : lower; + } + } + return number; + } + + /** + * The base implementation of `_.clone` and `_.cloneDeep` which tracks + * traversed objects. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} bitmask The bitmask flags. + * 1 - Deep clone + * 2 - Flatten inherited properties + * 4 - Clone symbols + * @param {Function} [customizer] The function to customize cloning. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The parent object of `value`. + * @param {Object} [stack] Tracks traversed objects and their clone counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, bitmask, customizer, key, object, stack) { + var result, + isDeep = bitmask & CLONE_DEEP_FLAG, + isFlat = bitmask & CLONE_FLAT_FLAG, + isFull = bitmask & CLONE_SYMBOLS_FLAG; + + if (customizer) { + result = object ? customizer(value, key, object, stack) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return copyArray(value, result); + } + } else { + var tag = getTag(value), + isFunc = tag == funcTag || tag == genTag; + + if (isBuffer(value)) { + return cloneBuffer(value, isDeep); + } + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + result = (isFlat || isFunc) ? {} : initCloneObject(value); + if (!isDeep) { + return isFlat + ? copySymbolsIn(value, baseAssignIn(result, value)) + : copySymbols(value, baseAssign(result, value)); + } + } else { + if (!cloneableTags[tag]) { + return object ? value : {}; + } + result = initCloneByTag(value, tag, isDeep); + } + } + // Check for circular references and return its corresponding clone. + stack || (stack = new Stack); + var stacked = stack.get(value); + if (stacked) { + return stacked; + } + stack.set(value, result); + + if (isSet(value)) { + value.forEach(function(subValue) { + result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)); + }); + } else if (isMap(value)) { + value.forEach(function(subValue, key) { + result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)); + }); + } + + var keysFunc = isFull + ? (isFlat ? getAllKeysIn : getAllKeys) + : (isFlat ? keysIn : keys); + + var props = isArr ? undefined : keysFunc(value); + arrayEach(props || value, function(subValue, key) { + if (props) { + key = subValue; + subValue = value[key]; + } + // Recursively populate clone (susceptible to call stack limits). + assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); + }); + return result; + } + + /** + * The base implementation of `_.conforms` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property predicates to conform to. + * @returns {Function} Returns the new spec function. + */ + function baseConforms(source) { + var props = keys(source); + return function(object) { + return baseConformsTo(object, source, props); + }; + } + + /** + * The base implementation of `_.conformsTo` which accepts `props` to check. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + */ + function baseConformsTo(object, source, props) { + var length = props.length; + if (object == null) { + return !length; + } + object = Object(object); + while (length--) { + var key = props[length], + predicate = source[key], + value = object[key]; + + if ((value === undefined && !(key in object)) || !predicate(value)) { + return false; + } + } + return true; + } + + /** + * The base implementation of `_.delay` and `_.defer` which accepts `args` + * to provide to `func`. + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {Array} args The arguments to provide to `func`. + * @returns {number|Object} Returns the timer id or timeout object. + */ + function baseDelay(func, wait, args) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return setTimeout(function() { func.apply(undefined, args); }, wait); + } + + /** + * The base implementation of methods like `_.difference` without support + * for excluding multiple arrays or iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Array} values The values to exclude. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + */ + function baseDifference(array, values, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + isCommon = true, + length = array.length, + result = [], + valuesLength = values.length; + + if (!length) { + return result; + } + if (iteratee) { + values = arrayMap(values, baseUnary(iteratee)); + } + if (comparator) { + includes = arrayIncludesWith; + isCommon = false; + } + else if (values.length >= LARGE_ARRAY_SIZE) { + includes = cacheHas; + isCommon = false; + values = new SetCache(values); + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee == null ? value : iteratee(value); + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var valuesIndex = valuesLength; + while (valuesIndex--) { + if (values[valuesIndex] === computed) { + continue outer; + } + } + result.push(value); + } + else if (!includes(values, computed, comparator)) { + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.forEach` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEach = createBaseEach(baseForOwn); + + /** + * The base implementation of `_.forEachRight` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEachRight = createBaseEach(baseForOwnRight, true); + + /** + * The base implementation of `_.every` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false` + */ + function baseEvery(collection, predicate) { + var result = true; + baseEach(collection, function(value, index, collection) { + result = !!predicate(value, index, collection); + return result; + }); + return result; + } + + /** + * The base implementation of methods like `_.max` and `_.min` which accepts a + * `comparator` to determine the extremum value. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per iteration. + * @param {Function} comparator The comparator used to compare values. + * @returns {*} Returns the extremum value. + */ + function baseExtremum(array, iteratee, comparator) { + var index = -1, + length = array.length; + + while (++index < length) { + var value = array[index], + current = iteratee(value); + + if (current != null && (computed === undefined + ? (current === current && !isSymbol(current)) + : comparator(current, computed) + )) { + var computed = current, + result = value; + } + } + return result; + } + + /** + * The base implementation of `_.fill` without an iteratee call guard. + * + * @private + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + */ + function baseFill(array, value, start, end) { + var length = array.length; + + start = toInteger(start); + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = (end === undefined || end > length) ? length : toInteger(end); + if (end < 0) { + end += length; + } + end = start > end ? 0 : toLength(end); + while (start < end) { + array[start++] = value; + } + return array; + } + + /** + * The base implementation of `_.filter` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function baseFilter(collection, predicate) { + var result = []; + baseEach(collection, function(value, index, collection) { + if (predicate(value, index, collection)) { + result.push(value); + } + }); + return result; + } + + /** + * The base implementation of `_.flatten` with support for restricting flattening. + * + * @private + * @param {Array} array The array to flatten. + * @param {number} depth The maximum recursion depth. + * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. + * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. + */ + function baseFlatten(array, depth, predicate, isStrict, result) { + var index = -1, + length = array.length; + + predicate || (predicate = isFlattenable); + result || (result = []); + + while (++index < length) { + var value = array[index]; + if (depth > 0 && predicate(value)) { + if (depth > 1) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, depth - 1, predicate, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; + } + } + return result; + } + + /** + * The base implementation of `baseForOwn` which iterates over `object` + * properties returned by `keysFunc` and invokes `iteratee` for each property. + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + /** + * This function is like `baseFor` except that it iterates over properties + * in the opposite order. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseForRight = createBaseFor(true); + + /** + * The base implementation of `_.forOwn` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwn(object, iteratee) { + return object && baseFor(object, iteratee, keys); + } + + /** + * The base implementation of `_.forOwnRight` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwnRight(object, iteratee) { + return object && baseForRight(object, iteratee, keys); + } + + /** + * The base implementation of `_.functions` which creates an array of + * `object` function property names filtered from `props`. + * + * @private + * @param {Object} object The object to inspect. + * @param {Array} props The property names to filter. + * @returns {Array} Returns the function names. + */ + function baseFunctions(object, props) { + return arrayFilter(props, function(key) { + return isFunction(object[key]); + }); + } + + /** + * The base implementation of `_.get` without support for default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path) { + path = castPath(path, object); + + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[toKey(path[index++])]; + } + return (index && index == length) ? object : undefined; + } + + /** + * The base implementation of `getAllKeys` and `getAllKeysIn` which uses + * `keysFunc` and `symbolsFunc` to get the enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Function} keysFunc The function to get the keys of `object`. + * @param {Function} symbolsFunc The function to get the symbols of `object`. + * @returns {Array} Returns the array of property names and symbols. + */ + function baseGetAllKeys(object, keysFunc, symbolsFunc) { + var result = keysFunc(object); + return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); + } + + /** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return (symToStringTag && symToStringTag in Object(value)) + ? getRawTag(value) + : objectToString(value); + } + + /** + * The base implementation of `_.gt` which doesn't coerce arguments. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + */ + function baseGt(value, other) { + return value > other; + } + + /** + * The base implementation of `_.has` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHas(object, key) { + return object != null && hasOwnProperty.call(object, key); + } + + /** + * The base implementation of `_.hasIn` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHasIn(object, key) { + return object != null && key in Object(object); + } + + /** + * The base implementation of `_.inRange` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to check. + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + */ + function baseInRange(number, start, end) { + return number >= nativeMin(start, end) && number < nativeMax(start, end); + } + + /** + * The base implementation of methods like `_.intersection`, without support + * for iteratee shorthands, that accepts an array of arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of shared values. + */ + function baseIntersection(arrays, iteratee, comparator) { + var includes = comparator ? arrayIncludesWith : arrayIncludes, + length = arrays[0].length, + othLength = arrays.length, + othIndex = othLength, + caches = Array(othLength), + maxLength = Infinity, + result = []; + + while (othIndex--) { + var array = arrays[othIndex]; + if (othIndex && iteratee) { + array = arrayMap(array, baseUnary(iteratee)); + } + maxLength = nativeMin(array.length, maxLength); + caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120)) + ? new SetCache(othIndex && array) + : undefined; + } + array = arrays[0]; + + var index = -1, + seen = caches[0]; + + outer: + while (++index < length && result.length < maxLength) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (!(seen + ? cacheHas(seen, computed) + : includes(result, computed, comparator) + )) { + othIndex = othLength; + while (--othIndex) { + var cache = caches[othIndex]; + if (!(cache + ? cacheHas(cache, computed) + : includes(arrays[othIndex], computed, comparator)) + ) { + continue outer; + } + } + if (seen) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.invert` and `_.invertBy` which inverts + * `object` with values transformed by `iteratee` and set by `setter`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform values. + * @param {Object} accumulator The initial inverted object. + * @returns {Function} Returns `accumulator`. + */ + function baseInverter(object, setter, iteratee, accumulator) { + baseForOwn(object, function(value, key, object) { + setter(accumulator, iteratee(value), key, object); + }); + return accumulator; + } + + /** + * The base implementation of `_.invoke` without support for individual + * method arguments. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + */ + function baseInvoke(object, path, args) { + path = castPath(path, object); + object = parent(object, path); + var func = object == null ? object : object[toKey(last(path))]; + return func == null ? undefined : apply(func, object, args); + } + + /** + * The base implementation of `_.isArguments`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + */ + function baseIsArguments(value) { + return isObjectLike(value) && baseGetTag(value) == argsTag; + } + + /** + * The base implementation of `_.isArrayBuffer` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + */ + function baseIsArrayBuffer(value) { + return isObjectLike(value) && baseGetTag(value) == arrayBufferTag; + } + + /** + * The base implementation of `_.isDate` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + */ + function baseIsDate(value) { + return isObjectLike(value) && baseGetTag(value) == dateTag; + } + + /** + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {boolean} bitmask The bitmask flags. + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Function} [customizer] The function to customize comparisons. + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, bitmask, customizer, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = objIsArr ? arrayTag : getTag(object), + othTag = othIsArr ? arrayTag : getTag(other); + + objTag = objTag == argsTag ? objectTag : objTag; + othTag = othTag == argsTag ? objectTag : othTag; + + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, + isSameTag = objTag == othTag; + + if (isSameTag && isBuffer(object)) { + if (!isBuffer(other)) { + return false; + } + objIsArr = true; + objIsObj = false; + } + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) + : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); + } + if (!(bitmask & COMPARE_PARTIAL_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, bitmask, customizer, equalFunc, stack); + } + + /** + * The base implementation of `_.isMap` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + */ + function baseIsMap(value) { + return isObjectLike(value) && getTag(value) == mapTag; + } + + /** + * The base implementation of `_.isMatch` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Array} matchData The property names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ + function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined + ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) + : result + )) { + return false; + } + } + } + return true; + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = isFunction(value) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.isRegExp` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + */ + function baseIsRegExp(value) { + return isObjectLike(value) && baseGetTag(value) == regexpTag; + } + + /** + * The base implementation of `_.isSet` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + */ + function baseIsSet(value) { + return isObjectLike(value) && getTag(value) == setTag; + } + + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; + } + + /** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ + function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); + } + + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeysIn(object) { + if (!isObject(object)) { + return nativeKeysIn(object); + } + var isProto = isPrototype(object), + result = []; + + for (var key in object) { + if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.lt` which doesn't coerce arguments. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + */ + function baseLt(value, other) { + return value < other; + } + + /** + * The base implementation of `_.map` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function baseMap(collection, iteratee) { + var index = -1, + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value, key, collection) { + result[++index] = iteratee(value, key, collection); + }); + return result; + } + + /** + * The base implementation of `_.matches` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); + } + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; + } + + /** + * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); + } + return function(object) { + var objValue = get(object, path); + return (objValue === undefined && objValue === srcValue) + ? hasIn(object, path) + : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); + }; + } + + /** + * The base implementation of `_.merge` without support for multiple sources. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {number} srcIndex The index of `source`. + * @param {Function} [customizer] The function to customize merged values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMerge(object, source, srcIndex, customizer, stack) { + if (object === source) { + return; + } + baseFor(source, function(srcValue, key) { + stack || (stack = new Stack); + if (isObject(srcValue)) { + baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); + } + else { + var newValue = customizer + ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack) + : undefined; + + if (newValue === undefined) { + newValue = srcValue; + } + assignMergeValue(object, key, newValue); + } + }, keysIn); + } + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {number} srcIndex The index of `source`. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize assigned values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { + var objValue = safeGet(object, key), + srcValue = safeGet(source, key), + stacked = stack.get(srcValue); + + if (stacked) { + assignMergeValue(object, key, stacked); + return; + } + var newValue = customizer + ? customizer(objValue, srcValue, (key + ''), object, source, stack) + : undefined; + + var isCommon = newValue === undefined; + + if (isCommon) { + var isArr = isArray(srcValue), + isBuff = !isArr && isBuffer(srcValue), + isTyped = !isArr && !isBuff && isTypedArray(srcValue); + + newValue = srcValue; + if (isArr || isBuff || isTyped) { + if (isArray(objValue)) { + newValue = objValue; + } + else if (isArrayLikeObject(objValue)) { + newValue = copyArray(objValue); + } + else if (isBuff) { + isCommon = false; + newValue = cloneBuffer(srcValue, true); + } + else if (isTyped) { + isCommon = false; + newValue = cloneTypedArray(srcValue, true); + } + else { + newValue = []; + } + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + newValue = objValue; + if (isArguments(objValue)) { + newValue = toPlainObject(objValue); + } + else if (!isObject(objValue) || isFunction(objValue)) { + newValue = initCloneObject(srcValue); + } + } + else { + isCommon = false; + } + } + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, newValue); + mergeFunc(newValue, srcValue, srcIndex, customizer, stack); + stack['delete'](srcValue); + } + assignMergeValue(object, key, newValue); + } + + /** + * The base implementation of `_.nth` which doesn't coerce arguments. + * + * @private + * @param {Array} array The array to query. + * @param {number} n The index of the element to return. + * @returns {*} Returns the nth element of `array`. + */ + function baseNth(array, n) { + var length = array.length; + if (!length) { + return; + } + n += n < 0 ? length : 0; + return isIndex(n, length) ? array[n] : undefined; + } + + /** + * The base implementation of `_.orderBy` without param guards. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {string[]} orders The sort orders of `iteratees`. + * @returns {Array} Returns the new sorted array. + */ + function baseOrderBy(collection, iteratees, orders) { + if (iteratees.length) { + iteratees = arrayMap(iteratees, function(iteratee) { + if (isArray(iteratee)) { + return function(value) { + return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee); + } + } + return iteratee; + }); + } else { + iteratees = [identity]; + } + + var index = -1; + iteratees = arrayMap(iteratees, baseUnary(getIteratee())); + + var result = baseMap(collection, function(value, key, collection) { + var criteria = arrayMap(iteratees, function(iteratee) { + return iteratee(value); + }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; + }); + + return baseSortBy(result, function(object, other) { + return compareMultiple(object, other, orders); + }); + } + + /** + * The base implementation of `_.pick` without support for individual + * property identifiers. + * + * @private + * @param {Object} object The source object. + * @param {string[]} paths The property paths to pick. + * @returns {Object} Returns the new object. + */ + function basePick(object, paths) { + return basePickBy(object, paths, function(value, path) { + return hasIn(object, path); + }); + } + + /** + * The base implementation of `_.pickBy` without support for iteratee shorthands. + * + * @private + * @param {Object} object The source object. + * @param {string[]} paths The property paths to pick. + * @param {Function} predicate The function invoked per property. + * @returns {Object} Returns the new object. + */ + function basePickBy(object, paths, predicate) { + var index = -1, + length = paths.length, + result = {}; + + while (++index < length) { + var path = paths[index], + value = baseGet(object, path); + + if (predicate(value, path)) { + baseSet(result, castPath(path, object), value); + } + } + return result; + } + + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; + } + + /** + * The base implementation of `_.pullAllBy` without support for iteratee + * shorthands. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + */ + function basePullAll(array, values, iteratee, comparator) { + var indexOf = comparator ? baseIndexOfWith : baseIndexOf, + index = -1, + length = values.length, + seen = array; + + if (array === values) { + values = copyArray(values); + } + if (iteratee) { + seen = arrayMap(array, baseUnary(iteratee)); + } + while (++index < length) { + var fromIndex = 0, + value = values[index], + computed = iteratee ? iteratee(value) : value; + + while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) { + if (seen !== array) { + splice.call(seen, fromIndex, 1); + } + splice.call(array, fromIndex, 1); + } + } + return array; + } + + /** + * The base implementation of `_.pullAt` without support for individual + * indexes or capturing the removed elements. + * + * @private + * @param {Array} array The array to modify. + * @param {number[]} indexes The indexes of elements to remove. + * @returns {Array} Returns `array`. + */ + function basePullAt(array, indexes) { + var length = array ? indexes.length : 0, + lastIndex = length - 1; + + while (length--) { + var index = indexes[length]; + if (length == lastIndex || index !== previous) { + var previous = index; + if (isIndex(index)) { + splice.call(array, index, 1); + } else { + baseUnset(array, index); + } + } + } + return array; + } + + /** + * The base implementation of `_.random` without support for returning + * floating-point numbers. + * + * @private + * @param {number} lower The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the random number. + */ + function baseRandom(lower, upper) { + return lower + nativeFloor(nativeRandom() * (upper - lower + 1)); + } + + /** + * The base implementation of `_.range` and `_.rangeRight` which doesn't + * coerce arguments. + * + * @private + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @param {number} step The value to increment or decrement by. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the range of numbers. + */ + function baseRange(start, end, step, fromRight) { + var index = -1, + length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), + result = Array(length); + + while (length--) { + result[fromRight ? length : ++index] = start; + start += step; + } + return result; + } + + /** + * The base implementation of `_.repeat` which doesn't coerce arguments. + * + * @private + * @param {string} string The string to repeat. + * @param {number} n The number of times to repeat the string. + * @returns {string} Returns the repeated string. + */ + function baseRepeat(string, n) { + var result = ''; + if (!string || n < 1 || n > MAX_SAFE_INTEGER) { + return result; + } + // Leverage the exponentiation by squaring algorithm for a faster repeat. + // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. + do { + if (n % 2) { + result += string; + } + n = nativeFloor(n / 2); + if (n) { + string += string; + } + } while (n); + + return result; + } + + /** + * The base implementation of `_.rest` which doesn't validate or coerce arguments. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + */ + function baseRest(func, start) { + return setToString(overRest(func, start, identity), func + ''); + } + + /** + * The base implementation of `_.sample`. + * + * @private + * @param {Array|Object} collection The collection to sample. + * @returns {*} Returns the random element. + */ + function baseSample(collection) { + return arraySample(values(collection)); + } + + /** + * The base implementation of `_.sampleSize` without param guards. + * + * @private + * @param {Array|Object} collection The collection to sample. + * @param {number} n The number of elements to sample. + * @returns {Array} Returns the random elements. + */ + function baseSampleSize(collection, n) { + var array = values(collection); + return shuffleSelf(array, baseClamp(n, 0, array.length)); + } + + /** + * The base implementation of `_.set`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ + function baseSet(object, path, value, customizer) { + if (!isObject(object)) { + return object; + } + path = castPath(path, object); + + var index = -1, + length = path.length, + lastIndex = length - 1, + nested = object; + + while (nested != null && ++index < length) { + var key = toKey(path[index]), + newValue = value; + + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + return object; + } + + if (index != lastIndex) { + var objValue = nested[key]; + newValue = customizer ? customizer(objValue, key, nested) : undefined; + if (newValue === undefined) { + newValue = isObject(objValue) + ? objValue + : (isIndex(path[index + 1]) ? [] : {}); + } + } + assignValue(nested, key, newValue); + nested = nested[key]; + } + return object; + } + + /** + * The base implementation of `setData` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var baseSetData = !metaMap ? identity : function(func, data) { + metaMap.set(func, data); + return func; + }; + + /** + * The base implementation of `setToString` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var baseSetToString = !defineProperty ? identity : function(func, string) { + return defineProperty(func, 'toString', { + 'configurable': true, + 'enumerable': false, + 'value': constant(string), + 'writable': true + }); + }; + + /** + * The base implementation of `_.shuffle`. + * + * @private + * @param {Array|Object} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + */ + function baseShuffle(collection) { + return shuffleSelf(values(collection)); + } + + /** + * The base implementation of `_.slice` without an iteratee call guard. + * + * @private + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function baseSlice(array, start, end) { + var index = -1, + length = array.length; + + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = end > length ? length : end; + if (end < 0) { + end += length; + } + length = start > end ? 0 : ((end - start) >>> 0); + start >>>= 0; + + var result = Array(length); + while (++index < length) { + result[index] = array[index + start]; + } + return result; + } + + /** + * The base implementation of `_.some` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function baseSome(collection, predicate) { + var result; + + baseEach(collection, function(value, index, collection) { + result = predicate(value, index, collection); + return !result; + }); + return !!result; + } + + /** + * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which + * performs a binary search of `array` to determine the index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function baseSortedIndex(array, value, retHighest) { + var low = 0, + high = array == null ? low : array.length; + + if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { + while (low < high) { + var mid = (low + high) >>> 1, + computed = array[mid]; + + if (computed !== null && !isSymbol(computed) && + (retHighest ? (computed <= value) : (computed < value))) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + return baseSortedIndexBy(array, value, identity, retHighest); + } + + /** + * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy` + * which invokes `iteratee` for `value` and each element of `array` to compute + * their sort ranking. The iteratee is invoked with one argument; (value). + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The iteratee invoked per element. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function baseSortedIndexBy(array, value, iteratee, retHighest) { + var low = 0, + high = array == null ? 0 : array.length; + if (high === 0) { + return 0; + } + + value = iteratee(value); + var valIsNaN = value !== value, + valIsNull = value === null, + valIsSymbol = isSymbol(value), + valIsUndefined = value === undefined; + + while (low < high) { + var mid = nativeFloor((low + high) / 2), + computed = iteratee(array[mid]), + othIsDefined = computed !== undefined, + othIsNull = computed === null, + othIsReflexive = computed === computed, + othIsSymbol = isSymbol(computed); + + if (valIsNaN) { + var setLow = retHighest || othIsReflexive; + } else if (valIsUndefined) { + setLow = othIsReflexive && (retHighest || othIsDefined); + } else if (valIsNull) { + setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); + } else if (valIsSymbol) { + setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); + } else if (othIsNull || othIsSymbol) { + setLow = false; + } else { + setLow = retHighest ? (computed <= value) : (computed < value); + } + if (setLow) { + low = mid + 1; + } else { + high = mid; + } + } + return nativeMin(high, MAX_ARRAY_INDEX); + } + + /** + * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseSortedUniq(array, iteratee) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + if (!index || !eq(computed, seen)) { + var seen = computed; + result[resIndex++] = value === 0 ? 0 : value; + } + } + return result; + } + + /** + * The base implementation of `_.toNumber` which doesn't ensure correct + * conversions of binary, hexadecimal, or octal string values. + * + * @private + * @param {*} value The value to process. + * @returns {number} Returns the number. + */ + function baseToNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + return +value; + } + + /** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isArray(value)) { + // Recursively convert values (susceptible to call stack limits). + return arrayMap(value, baseToString) + ''; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * The base implementation of `_.uniqBy` without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseUniq(array, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + length = array.length, + isCommon = true, + result = [], + seen = result; + + if (comparator) { + isCommon = false; + includes = arrayIncludesWith; + } + else if (length >= LARGE_ARRAY_SIZE) { + var set = iteratee ? null : createSet(array); + if (set) { + return setToArray(set); + } + isCommon = false; + includes = cacheHas; + seen = new SetCache; + } + else { + seen = iteratee ? [] : result; + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === computed) { + continue outer; + } + } + if (iteratee) { + seen.push(computed); + } + result.push(value); + } + else if (!includes(seen, computed, comparator)) { + if (seen !== result) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.unset`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The property path to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + */ + function baseUnset(object, path) { + path = castPath(path, object); + object = parent(object, path); + return object == null || delete object[toKey(last(path))]; + } + + /** + * The base implementation of `_.update`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to update. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ + function baseUpdate(object, path, updater, customizer) { + return baseSet(object, path, updater(baseGet(object, path)), customizer); + } + + /** + * The base implementation of methods like `_.dropWhile` and `_.takeWhile` + * without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [isDrop] Specify dropping elements instead of taking them. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the slice of `array`. + */ + function baseWhile(array, predicate, isDrop, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length) && + predicate(array[index], index, array)) {} + + return isDrop + ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) + : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); + } + + /** + * The base implementation of `wrapperValue` which returns the result of + * performing a sequence of actions on the unwrapped `value`, where each + * successive action is supplied the return value of the previous. + * + * @private + * @param {*} value The unwrapped value. + * @param {Array} actions Actions to perform to resolve the unwrapped value. + * @returns {*} Returns the resolved value. + */ + function baseWrapperValue(value, actions) { + var result = value; + if (result instanceof LazyWrapper) { + result = result.value(); + } + return arrayReduce(actions, function(result, action) { + return action.func.apply(action.thisArg, arrayPush([result], action.args)); + }, result); + } + + /** + * The base implementation of methods like `_.xor`, without support for + * iteratee shorthands, that accepts an array of arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of values. + */ + function baseXor(arrays, iteratee, comparator) { + var length = arrays.length; + if (length < 2) { + return length ? baseUniq(arrays[0]) : []; + } + var index = -1, + result = Array(length); + + while (++index < length) { + var array = arrays[index], + othIndex = -1; + + while (++othIndex < length) { + if (othIndex != index) { + result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator); + } + } + } + return baseUniq(baseFlatten(result, 1), iteratee, comparator); + } + + /** + * This base implementation of `_.zipObject` which assigns values using `assignFunc`. + * + * @private + * @param {Array} props The property identifiers. + * @param {Array} values The property values. + * @param {Function} assignFunc The function to assign values. + * @returns {Object} Returns the new object. + */ + function baseZipObject(props, values, assignFunc) { + var index = -1, + length = props.length, + valsLength = values.length, + result = {}; + + while (++index < length) { + var value = index < valsLength ? values[index] : undefined; + assignFunc(result, props[index], value); + } + return result; + } + + /** + * Casts `value` to an empty array if it's not an array like object. + * + * @private + * @param {*} value The value to inspect. + * @returns {Array|Object} Returns the cast array-like object. + */ + function castArrayLikeObject(value) { + return isArrayLikeObject(value) ? value : []; + } + + /** + * Casts `value` to `identity` if it's not a function. + * + * @private + * @param {*} value The value to inspect. + * @returns {Function} Returns cast function. + */ + function castFunction(value) { + return typeof value == 'function' ? value : identity; + } + + /** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @param {Object} [object] The object to query keys on. + * @returns {Array} Returns the cast property path array. + */ + function castPath(value, object) { + if (isArray(value)) { + return value; + } + return isKey(value, object) ? [value] : stringToPath(toString(value)); + } + + /** + * A `baseRest` alias which can be replaced with `identity` by module + * replacement plugins. + * + * @private + * @type {Function} + * @param {Function} func The function to apply a rest parameter to. + * @returns {Function} Returns the new function. + */ + var castRest = baseRest; + + /** + * Casts `array` to a slice if it's needed. + * + * @private + * @param {Array} array The array to inspect. + * @param {number} start The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the cast slice. + */ + function castSlice(array, start, end) { + var length = array.length; + end = end === undefined ? length : end; + return (!start && end >= length) ? array : baseSlice(array, start, end); + } + + /** + * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout). + * + * @private + * @param {number|Object} id The timer id or timeout object of the timer to clear. + */ + var clearTimeout = ctxClearTimeout || function(id) { + return root.clearTimeout(id); + }; + + /** + * Creates a clone of `buffer`. + * + * @private + * @param {Buffer} buffer The buffer to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Buffer} Returns the cloned buffer. + */ + function cloneBuffer(buffer, isDeep) { + if (isDeep) { + return buffer.slice(); + } + var length = buffer.length, + result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); + + buffer.copy(result); + return result; + } + + /** + * Creates a clone of `arrayBuffer`. + * + * @private + * @param {ArrayBuffer} arrayBuffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. + */ + function cloneArrayBuffer(arrayBuffer) { + var result = new arrayBuffer.constructor(arrayBuffer.byteLength); + new Uint8Array(result).set(new Uint8Array(arrayBuffer)); + return result; + } + + /** + * Creates a clone of `dataView`. + * + * @private + * @param {Object} dataView The data view to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned data view. + */ + function cloneDataView(dataView, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; + return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); + } + + /** + * Creates a clone of `regexp`. + * + * @private + * @param {Object} regexp The regexp to clone. + * @returns {Object} Returns the cloned regexp. + */ + function cloneRegExp(regexp) { + var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); + result.lastIndex = regexp.lastIndex; + return result; + } + + /** + * Creates a clone of the `symbol` object. + * + * @private + * @param {Object} symbol The symbol object to clone. + * @returns {Object} Returns the cloned symbol object. + */ + function cloneSymbol(symbol) { + return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; + } + + /** + * Creates a clone of `typedArray`. + * + * @private + * @param {Object} typedArray The typed array to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned typed array. + */ + function cloneTypedArray(typedArray, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; + return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); + } + + /** + * Compares values to sort them in ascending order. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */ + function compareAscending(value, other) { + if (value !== other) { + var valIsDefined = value !== undefined, + valIsNull = value === null, + valIsReflexive = value === value, + valIsSymbol = isSymbol(value); + + var othIsDefined = other !== undefined, + othIsNull = other === null, + othIsReflexive = other === other, + othIsSymbol = isSymbol(other); + + if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) || + (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) || + (valIsNull && othIsDefined && othIsReflexive) || + (!valIsDefined && othIsReflexive) || + !valIsReflexive) { + return 1; + } + if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) || + (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) || + (othIsNull && valIsDefined && valIsReflexive) || + (!othIsDefined && valIsReflexive) || + !othIsReflexive) { + return -1; + } + } + return 0; + } + + /** + * Used by `_.orderBy` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, + * specify an order of "desc" for descending or "asc" for ascending sort order + * of corresponding values. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]|string[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareMultiple(object, other, orders) { + var index = -1, + objCriteria = object.criteria, + othCriteria = other.criteria, + length = objCriteria.length, + ordersLength = orders.length; + + while (++index < length) { + var result = compareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * (order == 'desc' ? -1 : 1); + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; + } + + /** + * Creates an array that is the composition of partially applied arguments, + * placeholders, and provided arguments into a single array of arguments. + * + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to prepend to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgs(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersLength = holders.length, + leftIndex = -1, + leftLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(leftLength + rangeLength), + isUncurried = !isCurried; + + while (++leftIndex < leftLength) { + result[leftIndex] = partials[leftIndex]; + } + while (++argsIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[holders[argsIndex]] = args[argsIndex]; + } + } + while (rangeLength--) { + result[leftIndex++] = args[argsIndex++]; + } + return result; + } + + /** + * This function is like `composeArgs` except that the arguments composition + * is tailored for `_.partialRight`. + * + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to append to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgsRight(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersIndex = -1, + holdersLength = holders.length, + rightIndex = -1, + rightLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(rangeLength + rightLength), + isUncurried = !isCurried; + + while (++argsIndex < rangeLength) { + result[argsIndex] = args[argsIndex]; + } + var offset = argsIndex; + while (++rightIndex < rightLength) { + result[offset + rightIndex] = partials[rightIndex]; + } + while (++holdersIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[offset + holders[holdersIndex]] = args[argsIndex++]; + } + } + return result; + } + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function copyArray(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property identifiers to copy. + * @param {Object} [object={}] The object to copy properties to. + * @param {Function} [customizer] The function to customize copied values. + * @returns {Object} Returns `object`. + */ + function copyObject(source, props, object, customizer) { + var isNew = !object; + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + + var newValue = customizer + ? customizer(object[key], source[key], key, object, source) + : undefined; + + if (newValue === undefined) { + newValue = source[key]; + } + if (isNew) { + baseAssignValue(object, key, newValue); + } else { + assignValue(object, key, newValue); + } + } + return object; + } + + /** + * Copies own symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbols(source, object) { + return copyObject(source, getSymbols(source), object); + } + + /** + * Copies own and inherited symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbolsIn(source, object) { + return copyObject(source, getSymbolsIn(source), object); + } + + /** + * Creates a function like `_.groupBy`. + * + * @private + * @param {Function} setter The function to set accumulator values. + * @param {Function} [initializer] The accumulator object initializer. + * @returns {Function} Returns the new aggregator function. + */ + function createAggregator(setter, initializer) { + return function(collection, iteratee) { + var func = isArray(collection) ? arrayAggregator : baseAggregator, + accumulator = initializer ? initializer() : {}; + + return func(collection, setter, getIteratee(iteratee, 2), accumulator); + }; + } + + /** + * Creates a function like `_.assign`. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return baseRest(function(object, sources) { + var index = -1, + length = sources.length, + customizer = length > 1 ? sources[length - 1] : undefined, + guard = length > 2 ? sources[2] : undefined; + + customizer = (assigner.length > 3 && typeof customizer == 'function') + ? (length--, customizer) + : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + object = Object(object); + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, index, customizer); + } + } + return object; + }); + } + + /** + * Creates a `baseEach` or `baseEachRight` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + if (collection == null) { + return collection; + } + if (!isArrayLike(collection)) { + return eachFunc(collection, iteratee); + } + var length = collection.length, + index = fromRight ? length : -1, + iterable = Object(collection); + + while ((fromRight ? index-- : ++index < length)) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; + } + + /** + * Creates a base function for methods like `_.forIn` and `_.forOwn`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var index = -1, + iterable = Object(object), + props = keysFunc(object), + length = props.length; + + while (length--) { + var key = props[fromRight ? length : ++index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + /** + * Creates a function that wraps `func` to invoke it with the optional `this` + * binding of `thisArg`. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createBind(func, bitmask, thisArg) { + var isBind = bitmask & WRAP_BIND_FLAG, + Ctor = createCtor(func); + + function wrapper() { + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return fn.apply(isBind ? thisArg : this, arguments); + } + return wrapper; + } + + /** + * Creates a function like `_.lowerFirst`. + * + * @private + * @param {string} methodName The name of the `String` case method to use. + * @returns {Function} Returns the new case function. + */ + function createCaseFirst(methodName) { + return function(string) { + string = toString(string); + + var strSymbols = hasUnicode(string) + ? stringToArray(string) + : undefined; + + var chr = strSymbols + ? strSymbols[0] + : string.charAt(0); + + var trailing = strSymbols + ? castSlice(strSymbols, 1).join('') + : string.slice(1); + + return chr[methodName]() + trailing; + }; + } + + /** + * Creates a function like `_.camelCase`. + * + * @private + * @param {Function} callback The function to combine each word. + * @returns {Function} Returns the new compounder function. + */ + function createCompounder(callback) { + return function(string) { + return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); + }; + } + + /** + * Creates a function that produces an instance of `Ctor` regardless of + * whether it was invoked as part of a `new` expression or by `call` or `apply`. + * + * @private + * @param {Function} Ctor The constructor to wrap. + * @returns {Function} Returns the new wrapped function. + */ + function createCtor(Ctor) { + return function() { + // Use a `switch` statement to work with class constructors. See + // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist + // for more details. + var args = arguments; + switch (args.length) { + case 0: return new Ctor; + case 1: return new Ctor(args[0]); + case 2: return new Ctor(args[0], args[1]); + case 3: return new Ctor(args[0], args[1], args[2]); + case 4: return new Ctor(args[0], args[1], args[2], args[3]); + case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); + case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + } + var thisBinding = baseCreate(Ctor.prototype), + result = Ctor.apply(thisBinding, args); + + // Mimic the constructor's `return` behavior. + // See https://es5.github.io/#x13.2.2 for more details. + return isObject(result) ? result : thisBinding; + }; + } + + /** + * Creates a function that wraps `func` to enable currying. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {number} arity The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createCurry(func, bitmask, arity) { + var Ctor = createCtor(func); + + function wrapper() { + var length = arguments.length, + args = Array(length), + index = length, + placeholder = getHolder(wrapper); + + while (index--) { + args[index] = arguments[index]; + } + var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder) + ? [] + : replaceHolders(args, placeholder); + + length -= holders.length; + if (length < arity) { + return createRecurry( + func, bitmask, createHybrid, wrapper.placeholder, undefined, + args, holders, undefined, undefined, arity - length); + } + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return apply(fn, this, args); + } + return wrapper; + } + + /** + * Creates a `_.find` or `_.findLast` function. + * + * @private + * @param {Function} findIndexFunc The function to find the collection index. + * @returns {Function} Returns the new find function. + */ + function createFind(findIndexFunc) { + return function(collection, predicate, fromIndex) { + var iterable = Object(collection); + if (!isArrayLike(collection)) { + var iteratee = getIteratee(predicate, 3); + collection = keys(collection); + predicate = function(key) { return iteratee(iterable[key], key, iterable); }; + } + var index = findIndexFunc(collection, predicate, fromIndex); + return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined; + }; + } + + /** + * Creates a `_.flow` or `_.flowRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new flow function. + */ + function createFlow(fromRight) { + return flatRest(function(funcs) { + var length = funcs.length, + index = length, + prereq = LodashWrapper.prototype.thru; + + if (fromRight) { + funcs.reverse(); + } + while (index--) { + var func = funcs[index]; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (prereq && !wrapper && getFuncName(func) == 'wrapper') { + var wrapper = new LodashWrapper([], true); + } + } + index = wrapper ? index : length; + while (++index < length) { + func = funcs[index]; + + var funcName = getFuncName(func), + data = funcName == 'wrapper' ? getData(func) : undefined; + + if (data && isLaziable(data[0]) && + data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && + !data[4].length && data[9] == 1 + ) { + wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); + } else { + wrapper = (func.length == 1 && isLaziable(func)) + ? wrapper[funcName]() + : wrapper.thru(func); + } + } + return function() { + var args = arguments, + value = args[0]; + + if (wrapper && args.length == 1 && isArray(value)) { + return wrapper.plant(value).value(); + } + var index = 0, + result = length ? funcs[index].apply(this, args) : value; + + while (++index < length) { + result = funcs[index].call(this, result); + } + return result; + }; + }); + } + + /** + * Creates a function that wraps `func` to invoke it with optional `this` + * binding of `thisArg`, partial application, and currying. + * + * @private + * @param {Function|string} func The function or method name to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to + * the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [partialsRight] The arguments to append to those provided + * to the new function. + * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { + var isAry = bitmask & WRAP_ARY_FLAG, + isBind = bitmask & WRAP_BIND_FLAG, + isBindKey = bitmask & WRAP_BIND_KEY_FLAG, + isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG), + isFlip = bitmask & WRAP_FLIP_FLAG, + Ctor = isBindKey ? undefined : createCtor(func); + + function wrapper() { + var length = arguments.length, + args = Array(length), + index = length; + + while (index--) { + args[index] = arguments[index]; + } + if (isCurried) { + var placeholder = getHolder(wrapper), + holdersCount = countHolders(args, placeholder); + } + if (partials) { + args = composeArgs(args, partials, holders, isCurried); + } + if (partialsRight) { + args = composeArgsRight(args, partialsRight, holdersRight, isCurried); + } + length -= holdersCount; + if (isCurried && length < arity) { + var newHolders = replaceHolders(args, placeholder); + return createRecurry( + func, bitmask, createHybrid, wrapper.placeholder, thisArg, + args, newHolders, argPos, ary, arity - length + ); + } + var thisBinding = isBind ? thisArg : this, + fn = isBindKey ? thisBinding[func] : func; + + length = args.length; + if (argPos) { + args = reorder(args, argPos); + } else if (isFlip && length > 1) { + args.reverse(); + } + if (isAry && ary < length) { + args.length = ary; + } + if (this && this !== root && this instanceof wrapper) { + fn = Ctor || createCtor(fn); + } + return fn.apply(thisBinding, args); + } + return wrapper; + } + + /** + * Creates a function like `_.invertBy`. + * + * @private + * @param {Function} setter The function to set accumulator values. + * @param {Function} toIteratee The function to resolve iteratees. + * @returns {Function} Returns the new inverter function. + */ + function createInverter(setter, toIteratee) { + return function(object, iteratee) { + return baseInverter(object, setter, toIteratee(iteratee), {}); + }; + } + + /** + * Creates a function that performs a mathematical operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @param {number} [defaultValue] The value used for `undefined` arguments. + * @returns {Function} Returns the new mathematical operation function. + */ + function createMathOperation(operator, defaultValue) { + return function(value, other) { + var result; + if (value === undefined && other === undefined) { + return defaultValue; + } + if (value !== undefined) { + result = value; + } + if (other !== undefined) { + if (result === undefined) { + return other; + } + if (typeof value == 'string' || typeof other == 'string') { + value = baseToString(value); + other = baseToString(other); + } else { + value = baseToNumber(value); + other = baseToNumber(other); + } + result = operator(value, other); + } + return result; + }; + } + + /** + * Creates a function like `_.over`. + * + * @private + * @param {Function} arrayFunc The function to iterate over iteratees. + * @returns {Function} Returns the new over function. + */ + function createOver(arrayFunc) { + return flatRest(function(iteratees) { + iteratees = arrayMap(iteratees, baseUnary(getIteratee())); + return baseRest(function(args) { + var thisArg = this; + return arrayFunc(iteratees, function(iteratee) { + return apply(iteratee, thisArg, args); + }); + }); + }); + } + + /** + * Creates the padding for `string` based on `length`. The `chars` string + * is truncated if the number of characters exceeds `length`. + * + * @private + * @param {number} length The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padding for `string`. + */ + function createPadding(length, chars) { + chars = chars === undefined ? ' ' : baseToString(chars); + + var charsLength = chars.length; + if (charsLength < 2) { + return charsLength ? baseRepeat(chars, length) : chars; + } + var result = baseRepeat(chars, nativeCeil(length / stringSize(chars))); + return hasUnicode(chars) + ? castSlice(stringToArray(result), 0, length).join('') + : result.slice(0, length); + } + + /** + * Creates a function that wraps `func` to invoke it with the `this` binding + * of `thisArg` and `partials` prepended to the arguments it receives. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} partials The arguments to prepend to those provided to + * the new function. + * @returns {Function} Returns the new wrapped function. + */ + function createPartial(func, bitmask, thisArg, partials) { + var isBind = bitmask & WRAP_BIND_FLAG, + Ctor = createCtor(func); + + function wrapper() { + var argsIndex = -1, + argsLength = arguments.length, + leftIndex = -1, + leftLength = partials.length, + args = Array(leftLength + argsLength), + fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + + while (++leftIndex < leftLength) { + args[leftIndex] = partials[leftIndex]; + } + while (argsLength--) { + args[leftIndex++] = arguments[++argsIndex]; + } + return apply(fn, isBind ? thisArg : this, args); + } + return wrapper; + } + + /** + * Creates a `_.range` or `_.rangeRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new range function. + */ + function createRange(fromRight) { + return function(start, end, step) { + if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { + end = step = undefined; + } + // Ensure the sign of `-0` is preserved. + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + step = step === undefined ? (start < end ? 1 : -1) : toFinite(step); + return baseRange(start, end, step, fromRight); + }; + } + + /** + * Creates a function that performs a relational operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @returns {Function} Returns the new relational operation function. + */ + function createRelationalOperation(operator) { + return function(value, other) { + if (!(typeof value == 'string' && typeof other == 'string')) { + value = toNumber(value); + other = toNumber(other); + } + return operator(value, other); + }; + } + + /** + * Creates a function that wraps `func` to continue currying. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {Function} wrapFunc The function to create the `func` wrapper. + * @param {*} placeholder The placeholder value. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to + * the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) { + var isCurry = bitmask & WRAP_CURRY_FLAG, + newHolders = isCurry ? holders : undefined, + newHoldersRight = isCurry ? undefined : holders, + newPartials = isCurry ? partials : undefined, + newPartialsRight = isCurry ? undefined : partials; + + bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG); + bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG); + + if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) { + bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG); + } + var newData = [ + func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, + newHoldersRight, argPos, ary, arity + ]; + + var result = wrapFunc.apply(undefined, newData); + if (isLaziable(func)) { + setData(result, newData); + } + result.placeholder = placeholder; + return setWrapToString(result, func, bitmask); + } + + /** + * Creates a function like `_.round`. + * + * @private + * @param {string} methodName The name of the `Math` method to use when rounding. + * @returns {Function} Returns the new round function. + */ + function createRound(methodName) { + var func = Math[methodName]; + return function(number, precision) { + number = toNumber(number); + precision = precision == null ? 0 : nativeMin(toInteger(precision), 292); + if (precision && nativeIsFinite(number)) { + // Shift with exponential notation to avoid floating-point issues. + // See [MDN](https://mdn.io/round#Examples) for more details. + var pair = (toString(number) + 'e').split('e'), + value = func(pair[0] + 'e' + (+pair[1] + precision)); + + pair = (toString(value) + 'e').split('e'); + return +(pair[0] + 'e' + (+pair[1] - precision)); + } + return func(number); + }; + } + + /** + * Creates a set object of `values`. + * + * @private + * @param {Array} values The values to add to the set. + * @returns {Object} Returns the new set. + */ + var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) { + return new Set(values); + }; + + /** + * Creates a `_.toPairs` or `_.toPairsIn` function. + * + * @private + * @param {Function} keysFunc The function to get the keys of a given object. + * @returns {Function} Returns the new pairs function. + */ + function createToPairs(keysFunc) { + return function(object) { + var tag = getTag(object); + if (tag == mapTag) { + return mapToArray(object); + } + if (tag == setTag) { + return setToPairs(object); + } + return baseToPairs(object, keysFunc(object)); + }; + } + + /** + * Creates a function that either curries or invokes `func` with optional + * `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to wrap. + * @param {number} bitmask The bitmask flags. + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` or `_.curryRight` of a bound function + * 8 - `_.curry` + * 16 - `_.curryRight` + * 32 - `_.partial` + * 64 - `_.partialRight` + * 128 - `_.rearg` + * 256 - `_.ary` + * 512 - `_.flip` + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to be partially applied. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { + var isBindKey = bitmask & WRAP_BIND_KEY_FLAG; + if (!isBindKey && typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = partials ? partials.length : 0; + if (!length) { + bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG); + partials = holders = undefined; + } + ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0); + arity = arity === undefined ? arity : toInteger(arity); + length -= holders ? holders.length : 0; + + if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) { + var partialsRight = partials, + holdersRight = holders; + + partials = holders = undefined; + } + var data = isBindKey ? undefined : getData(func); + + var newData = [ + func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, + argPos, ary, arity + ]; + + if (data) { + mergeData(newData, data); + } + func = newData[0]; + bitmask = newData[1]; + thisArg = newData[2]; + partials = newData[3]; + holders = newData[4]; + arity = newData[9] = newData[9] === undefined + ? (isBindKey ? 0 : func.length) + : nativeMax(newData[9] - length, 0); + + if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) { + bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG); + } + if (!bitmask || bitmask == WRAP_BIND_FLAG) { + var result = createBind(func, bitmask, thisArg); + } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) { + result = createCurry(func, bitmask, arity); + } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) { + result = createPartial(func, bitmask, thisArg, partials); + } else { + result = createHybrid.apply(undefined, newData); + } + var setter = data ? baseSetData : setData; + return setWrapToString(setter(result, newData), func, bitmask); + } + + /** + * Used by `_.defaults` to customize its `_.assignIn` use to assign properties + * of source objects to the destination object for all destination properties + * that resolve to `undefined`. + * + * @private + * @param {*} objValue The destination value. + * @param {*} srcValue The source value. + * @param {string} key The key of the property to assign. + * @param {Object} object The parent object of `objValue`. + * @returns {*} Returns the value to assign. + */ + function customDefaultsAssignIn(objValue, srcValue, key, object) { + if (objValue === undefined || + (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) { + return srcValue; + } + return objValue; + } + + /** + * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source + * objects into destination objects that are passed thru. + * + * @private + * @param {*} objValue The destination value. + * @param {*} srcValue The source value. + * @param {string} key The key of the property to merge. + * @param {Object} object The parent object of `objValue`. + * @param {Object} source The parent object of `srcValue`. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + * @returns {*} Returns the value to assign. + */ + function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { + if (isObject(objValue) && isObject(srcValue)) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, objValue); + baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack); + stack['delete'](srcValue); + } + return objValue; + } + + /** + * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain + * objects. + * + * @private + * @param {*} value The value to inspect. + * @param {string} key The key of the property to inspect. + * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`. + */ + function customOmitClone(value) { + return isPlainObject(value) ? undefined : value; + } + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + // Check that cyclic values are equal. + var arrStacked = stack.get(array); + var othStacked = stack.get(other); + if (arrStacked && othStacked) { + return arrStacked == other && othStacked == array; + } + var index = -1, + result = true, + seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined; + + stack.set(array, other); + stack.set(other, array); + + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!arraySome(other, function(othValue, othIndex) { + if (!cacheHas(seen, othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { + return seen.push(othIndex); + } + })) { + result = false; + break; + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, bitmask, customizer, stack) + )) { + result = false; + break; + } + } + stack['delete'](array); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength != other.byteLength) || + (object.byteOffset != other.byteOffset)) { + return false; + } + object = object.buffer; + other = other.buffer; + + case arrayBufferTag: + if ((object.byteLength != other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false; + } + return true; + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other); + + case errorTag: + return object.name == other.name && object.message == other.message; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object == (other + ''); + + case mapTag: + var convert = mapToArray; + + case setTag: + var isPartial = bitmask & COMPARE_PARTIAL_FLAG; + convert || (convert = setToArray); + + if (object.size != other.size && !isPartial) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= COMPARE_UNORDERED_FLAG; + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); + stack['delete'](object); + return result; + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + objProps = getAllKeys(object), + objLength = objProps.length, + othProps = getAllKeys(other), + othLength = othProps.length; + + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + // Check that cyclic values are equal. + var objStacked = stack.get(object); + var othStacked = stack.get(other); + if (objStacked && othStacked) { + return objStacked == other && othStacked == object; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack); + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) + : compared + )) { + result = false; + break; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (result && !skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + result = false; + } + } + stack['delete'](object); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseRest` which flattens the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @returns {Function} Returns the new function. + */ + function flatRest(func) { + return setToString(overRest(func, undefined, flatten), func + ''); + } + + /** + * Creates an array of own enumerable property names and symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeys(object) { + return baseGetAllKeys(object, keys, getSymbols); + } + + /** + * Creates an array of own and inherited enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeysIn(object) { + return baseGetAllKeys(object, keysIn, getSymbolsIn); + } + + /** + * Gets metadata for `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {*} Returns the metadata for `func`. + */ + var getData = !metaMap ? noop : function(func) { + return metaMap.get(func); + }; + + /** + * Gets the name of `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {string} Returns the function name. + */ + function getFuncName(func) { + var result = (func.name + ''), + array = realNames[result], + length = hasOwnProperty.call(realNames, result) ? array.length : 0; + + while (length--) { + var data = array[length], + otherFunc = data.func; + if (otherFunc == null || otherFunc == func) { + return data.name; + } + } + return result; + } + + /** + * Gets the argument placeholder value for `func`. + * + * @private + * @param {Function} func The function to inspect. + * @returns {*} Returns the placeholder value. + */ + function getHolder(func) { + var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; + return object.placeholder; + } + + /** + * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, + * this function returns the custom method, otherwise it returns `baseIteratee`. + * If arguments are provided, the chosen function is invoked with them and + * its result is returned. + * + * @private + * @param {*} [value] The value to convert to an iteratee. + * @param {number} [arity] The arity of the created iteratee. + * @returns {Function} Returns the chosen function or its result. + */ + function getIteratee() { + var result = lodash.iteratee || iteratee; + result = result === iteratee ? baseIteratee : result; + return arguments.length ? result(arguments[0], arguments[1]) : result; + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the property names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ + function getMatchData(object) { + var result = keys(object), + length = result.length; + + while (length--) { + var key = result[length], + value = object[key]; + + result[length] = [key, value, isStrictComparable(value)]; + } + return result; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ + function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + + try { + value[symToStringTag] = undefined; + var unmasked = true; + } catch (e) {} + + var result = nativeObjectToString.call(value); + if (unmasked) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } + } + return result; + } + + /** + * Creates an array of the own enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = !nativeGetSymbols ? stubArray : function(object) { + if (object == null) { + return []; + } + object = Object(object); + return arrayFilter(nativeGetSymbols(object), function(symbol) { + return propertyIsEnumerable.call(object, symbol); + }); + }; + + /** + * Creates an array of the own and inherited enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { + var result = []; + while (object) { + arrayPush(result, getSymbols(object)); + object = getPrototype(object); + } + return result; + }; + + /** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + var getTag = baseGetTag; + + // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. + if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = baseGetTag(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : ''; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; + } + + /** + * Gets the view, applying any `transforms` to the `start` and `end` positions. + * + * @private + * @param {number} start The start of the view. + * @param {number} end The end of the view. + * @param {Array} transforms The transformations to apply to the view. + * @returns {Object} Returns an object containing the `start` and `end` + * positions of the view. + */ + function getView(start, end, transforms) { + var index = -1, + length = transforms.length; + + while (++index < length) { + var data = transforms[index], + size = data.size; + + switch (data.type) { + case 'drop': start += size; break; + case 'dropRight': end -= size; break; + case 'take': end = nativeMin(end, start + size); break; + case 'takeRight': start = nativeMax(start, end - size); break; + } + } + return { 'start': start, 'end': end }; + } + + /** + * Extracts wrapper details from the `source` body comment. + * + * @private + * @param {string} source The source to inspect. + * @returns {Array} Returns the wrapper details. + */ + function getWrapDetails(source) { + var match = source.match(reWrapDetails); + return match ? match[1].split(reSplitDetails) : []; + } + + /** + * Checks if `path` exists on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @param {Function} hasFunc The function to check properties. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + */ + function hasPath(object, path, hasFunc) { + path = castPath(path, object); + + var index = -1, + length = path.length, + result = false; + + while (++index < length) { + var key = toKey(path[index]); + if (!(result = object != null && hasFunc(object, key))) { + break; + } + object = object[key]; + } + if (result || ++index != length) { + return result; + } + length = object == null ? 0 : object.length; + return !!length && isLength(length) && isIndex(key, length) && + (isArray(object) || isArguments(object)); + } + + /** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ + function initCloneArray(array) { + var length = array.length, + result = new array.constructor(length); + + // Add properties assigned by `RegExp#exec`. + if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index; + result.input = array.input; + } + return result; + } + + /** + * Initializes an object clone. + * + * @private + * @param {Object} object The object to clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneObject(object) { + return (typeof object.constructor == 'function' && !isPrototype(object)) + ? baseCreate(getPrototype(object)) + : {}; + } + + /** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneByTag(object, tag, isDeep) { + var Ctor = object.constructor; + switch (tag) { + case arrayBufferTag: + return cloneArrayBuffer(object); + + case boolTag: + case dateTag: + return new Ctor(+object); + + case dataViewTag: + return cloneDataView(object, isDeep); + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + return cloneTypedArray(object, isDeep); + + case mapTag: + return new Ctor; + + case numberTag: + case stringTag: + return new Ctor(object); + + case regexpTag: + return cloneRegExp(object); + + case setTag: + return new Ctor; + + case symbolTag: + return cloneSymbol(object); + } + } + + /** + * Inserts wrapper `details` in a comment at the top of the `source` body. + * + * @private + * @param {string} source The source to modify. + * @returns {Array} details The details to insert. + * @returns {string} Returns the modified source. + */ + function insertWrapDetails(source, details) { + var length = details.length; + if (!length) { + return source; + } + var lastIndex = length - 1; + details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; + details = details.join(length > 2 ? ', ' : ' '); + return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); + } + + /** + * Checks if `value` is a flattenable `arguments` object or array. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. + */ + function isFlattenable(value) { + return isArray(value) || isArguments(value) || + !!(spreadableSymbol && value && value[spreadableSymbol]); + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + var type = typeof value; + length = length == null ? MAX_SAFE_INTEGER : length; + + return !!length && + (type == 'number' || + (type != 'symbol' && reIsUint.test(value))) && + (value > -1 && value % 1 == 0 && value < length); + } + + /** + * Checks if the given arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, + * else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object) + ) { + return eq(object[index], value); + } + return false; + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has a lazy counterpart. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` has a lazy counterpart, + * else `false`. + */ + function isLaziable(func) { + var funcName = getFuncName(func), + other = lodash[funcName]; + + if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) { + return false; + } + if (func === other) { + return true; + } + var data = getData(other); + return !!data && func === data[0]; + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Checks if `func` is capable of being masked. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `func` is maskable, else `false`. + */ + var isMaskable = coreJsData ? isFunction : stubFalse; + + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + + /** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ + function isStrictComparable(value) { + return value === value && !isObject(value); + } + + /** + * A specialized version of `matchesProperty` for source values suitable + * for strict equality comparisons, i.e. `===`. + * + * @private + * @param {string} key The key of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function matchesStrictComparable(key, srcValue) { + return function(object) { + if (object == null) { + return false; + } + return object[key] === srcValue && + (srcValue !== undefined || (key in Object(object))); + }; + } + + /** + * A specialized version of `_.memoize` which clears the memoized function's + * cache when it exceeds `MAX_MEMOIZE_SIZE`. + * + * @private + * @param {Function} func The function to have its output memoized. + * @returns {Function} Returns the new memoized function. + */ + function memoizeCapped(func) { + var result = memoize(func, function(key) { + if (cache.size === MAX_MEMOIZE_SIZE) { + cache.clear(); + } + return key; + }); + + var cache = result.cache; + return result; + } + + /** + * Merges the function metadata of `source` into `data`. + * + * Merging metadata reduces the number of wrappers used to invoke a function. + * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` + * may be applied regardless of execution order. Methods like `_.ary` and + * `_.rearg` modify function arguments, making the order in which they are + * executed important, preventing the merging of metadata. However, we make + * an exception for a safe combined case where curried functions have `_.ary` + * and or `_.rearg` applied. + * + * @private + * @param {Array} data The destination metadata. + * @param {Array} source The source metadata. + * @returns {Array} Returns `data`. + */ + function mergeData(data, source) { + var bitmask = data[1], + srcBitmask = source[1], + newBitmask = bitmask | srcBitmask, + isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG); + + var isCombo = + ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) || + ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) || + ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG)); + + // Exit early if metadata can't be merged. + if (!(isCommon || isCombo)) { + return data; + } + // Use source `thisArg` if available. + if (srcBitmask & WRAP_BIND_FLAG) { + data[2] = source[2]; + // Set when currying a bound function. + newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG; + } + // Compose partial arguments. + var value = source[3]; + if (value) { + var partials = data[3]; + data[3] = partials ? composeArgs(partials, value, source[4]) : value; + data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4]; + } + // Compose partial right arguments. + value = source[5]; + if (value) { + partials = data[5]; + data[5] = partials ? composeArgsRight(partials, value, source[6]) : value; + data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6]; + } + // Use source `argPos` if available. + value = source[7]; + if (value) { + data[7] = value; + } + // Use source `ary` if it's smaller. + if (srcBitmask & WRAP_ARY_FLAG) { + data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); + } + // Use source `arity` if one is not provided. + if (data[9] == null) { + data[9] = source[9]; + } + // Use source `func` and merge bitmasks. + data[0] = source[0]; + data[1] = newBitmask; + + return data; + } + + /** + * This function is like + * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * except that it includes inherited enumerable properties. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function nativeKeysIn(object) { + var result = []; + if (object != null) { + for (var key in Object(object)) { + result.push(key); + } + } + return result; + } + + /** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ + function objectToString(value) { + return nativeObjectToString.call(value); + } + + /** + * A specialized version of `baseRest` which transforms the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @param {Function} transform The rest array transform. + * @returns {Function} Returns the new function. + */ + function overRest(func, start, transform) { + start = nativeMax(start === undefined ? (func.length - 1) : start, 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + array = Array(length); + + while (++index < length) { + array[index] = args[start + index]; + } + index = -1; + var otherArgs = Array(start + 1); + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = transform(array); + return apply(func, this, otherArgs); + }; + } + + /** + * Gets the parent value at `path` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} path The path to get the parent value of. + * @returns {*} Returns the parent value. + */ + function parent(object, path) { + return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1)); + } + + /** + * Reorder `array` according to the specified indexes where the element at + * the first index is assigned as the first element, the element at + * the second index is assigned as the second element, and so on. + * + * @private + * @param {Array} array The array to reorder. + * @param {Array} indexes The arranged array indexes. + * @returns {Array} Returns `array`. + */ + function reorder(array, indexes) { + var arrLength = array.length, + length = nativeMin(indexes.length, arrLength), + oldArray = copyArray(array); + + while (length--) { + var index = indexes[length]; + array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; + } + return array; + } + + /** + * Gets the value at `key`, unless `key` is "__proto__" or "constructor". + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function safeGet(object, key) { + if (key === 'constructor' && typeof object[key] === 'function') { + return; + } + + if (key == '__proto__') { + return; + } + + return object[key]; + } + + /** + * Sets metadata for `func`. + * + * **Note:** If this function becomes hot, i.e. is invoked a lot in a short + * period of time, it will trip its breaker and transition to an identity + * function to avoid garbage collection pauses in V8. See + * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070) + * for more details. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var setData = shortOut(baseSetData); + + /** + * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout). + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @returns {number|Object} Returns the timer id or timeout object. + */ + var setTimeout = ctxSetTimeout || function(func, wait) { + return root.setTimeout(func, wait); + }; + + /** + * Sets the `toString` method of `func` to return `string`. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var setToString = shortOut(baseSetToString); + + /** + * Sets the `toString` method of `wrapper` to mimic the source of `reference` + * with wrapper details in a comment at the top of the source body. + * + * @private + * @param {Function} wrapper The function to modify. + * @param {Function} reference The reference function. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Function} Returns `wrapper`. + */ + function setWrapToString(wrapper, reference, bitmask) { + var source = (reference + ''); + return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask))); + } + + /** + * Creates a function that'll short out and invoke `identity` instead + * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` + * milliseconds. + * + * @private + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new shortable function. + */ + function shortOut(func) { + var count = 0, + lastCalled = 0; + + return function() { + var stamp = nativeNow(), + remaining = HOT_SPAN - (stamp - lastCalled); + + lastCalled = stamp; + if (remaining > 0) { + if (++count >= HOT_COUNT) { + return arguments[0]; + } + } else { + count = 0; + } + return func.apply(undefined, arguments); + }; + } + + /** + * A specialized version of `_.shuffle` which mutates and sets the size of `array`. + * + * @private + * @param {Array} array The array to shuffle. + * @param {number} [size=array.length] The size of `array`. + * @returns {Array} Returns `array`. + */ + function shuffleSelf(array, size) { + var index = -1, + length = array.length, + lastIndex = length - 1; + + size = size === undefined ? length : size; + while (++index < size) { + var rand = baseRandom(index, lastIndex), + value = array[rand]; + + array[rand] = array[index]; + array[index] = value; + } + array.length = size; + return array; + } + + /** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ + var stringToPath = memoizeCapped(function(string) { + var result = []; + if (string.charCodeAt(0) === 46 /* . */) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, subString) { + result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + }); + + /** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ + function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to convert. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * Updates wrapper `details` based on `bitmask` flags. + * + * @private + * @returns {Array} details The details to modify. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Array} Returns `details`. + */ + function updateWrapDetails(details, bitmask) { + arrayEach(wrapFlags, function(pair) { + var value = '_.' + pair[0]; + if ((bitmask & pair[1]) && !arrayIncludes(details, value)) { + details.push(value); + } + }); + return details.sort(); + } + + /** + * Creates a clone of `wrapper`. + * + * @private + * @param {Object} wrapper The wrapper to clone. + * @returns {Object} Returns the cloned wrapper. + */ + function wrapperClone(wrapper) { + if (wrapper instanceof LazyWrapper) { + return wrapper.clone(); + } + var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__); + result.__actions__ = copyArray(wrapper.__actions__); + result.__index__ = wrapper.__index__; + result.__values__ = wrapper.__values__; + return result; + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of elements split into groups the length of `size`. + * If `array` can't be split evenly, the final chunk will be the remaining + * elements. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to process. + * @param {number} [size=1] The length of each chunk + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the new array of chunks. + * @example + * + * _.chunk(['a', 'b', 'c', 'd'], 2); + * // => [['a', 'b'], ['c', 'd']] + * + * _.chunk(['a', 'b', 'c', 'd'], 3); + * // => [['a', 'b', 'c'], ['d']] + */ + function chunk(array, size, guard) { + if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) { + size = 1; + } else { + size = nativeMax(toInteger(size), 0); + } + var length = array == null ? 0 : array.length; + if (!length || size < 1) { + return []; + } + var index = 0, + resIndex = 0, + result = Array(nativeCeil(length / size)); + + while (index < length) { + result[resIndex++] = baseSlice(array, index, (index += size)); + } + return result; + } + + /** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are falsey. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to compact. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * Creates a new array concatenating `array` with any additional arrays + * and/or values. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to concatenate. + * @param {...*} [values] The values to concatenate. + * @returns {Array} Returns the new concatenated array. + * @example + * + * var array = [1]; + * var other = _.concat(array, 2, [3], [[4]]); + * + * console.log(other); + * // => [1, 2, 3, [4]] + * + * console.log(array); + * // => [1] + */ + function concat() { + var length = arguments.length; + if (!length) { + return []; + } + var args = Array(length - 1), + array = arguments[0], + index = length; + + while (index--) { + args[index - 1] = arguments[index]; + } + return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)); + } + + /** + * Creates an array of `array` values not included in the other given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * **Note:** Unlike `_.pullAll`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see _.without, _.xor + * @example + * + * _.difference([2, 1], [2, 3]); + * // => [1] + */ + var difference = baseRest(function(array, values) { + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) + : []; + }); + + /** + * This method is like `_.difference` except that it accepts `iteratee` which + * is invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * **Note:** Unlike `_.pullAllBy`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [1.2] + * + * // The `_.property` iteratee shorthand. + * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); + * // => [{ 'x': 2 }] + */ + var differenceBy = baseRest(function(array, values) { + var iteratee = last(values); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) + : []; + }); + + /** + * This method is like `_.difference` except that it accepts `comparator` + * which is invoked to compare elements of `array` to `values`. The order and + * references of result values are determined by the first array. The comparator + * is invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `_.pullAllWith`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * + * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual); + * // => [{ 'x': 2, 'y': 1 }] + */ + var differenceWith = baseRest(function(array, values) { + var comparator = last(values); + if (isArrayLikeObject(comparator)) { + comparator = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) + : []; + }); + + /** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.drop([1, 2, 3]); + * // => [2, 3] + * + * _.drop([1, 2, 3], 2); + * // => [3] + * + * _.drop([1, 2, 3], 5); + * // => [] + * + * _.drop([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function drop(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + return baseSlice(array, n < 0 ? 0 : n, length); + } + + /** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRight([1, 2, 3]); + * // => [1, 2] + * + * _.dropRight([1, 2, 3], 2); + * // => [1] + * + * _.dropRight([1, 2, 3], 5); + * // => [] + * + * _.dropRight([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function dropRight(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + n = length - n; + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.dropRightWhile(users, function(o) { return !o.active; }); + * // => objects for ['barney'] + * + * // The `_.matches` iteratee shorthand. + * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); + * // => objects for ['barney', 'fred'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.dropRightWhile(users, ['active', false]); + * // => objects for ['barney'] + * + * // The `_.property` iteratee shorthand. + * _.dropRightWhile(users, 'active'); + * // => objects for ['barney', 'fred', 'pebbles'] + */ + function dropRightWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), true, true) + : []; + } + + /** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.dropWhile(users, function(o) { return !o.active; }); + * // => objects for ['pebbles'] + * + * // The `_.matches` iteratee shorthand. + * _.dropWhile(users, { 'user': 'barney', 'active': false }); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.dropWhile(users, ['active', false]); + * // => objects for ['pebbles'] + * + * // The `_.property` iteratee shorthand. + * _.dropWhile(users, 'active'); + * // => objects for ['barney', 'fred', 'pebbles'] + */ + function dropWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), true) + : []; + } + + /** + * Fills elements of `array` with `value` from `start` up to, but not + * including, `end`. + * + * **Note:** This method mutates `array`. + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Array + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.fill(array, 'a'); + * console.log(array); + * // => ['a', 'a', 'a'] + * + * _.fill(Array(3), 2); + * // => [2, 2, 2] + * + * _.fill([4, 6, 8, 10], '*', 1, 3); + * // => [4, '*', '*', 10] + */ + function fill(array, value, start, end) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { + start = 0; + end = length; + } + return baseFill(array, value, start, end); + } + + /** + * This method is like `_.find` except that it returns the index of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, function(o) { return o.user == 'barney'; }); + * // => 0 + * + * // The `_.matches` iteratee shorthand. + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findIndex(users, ['active', false]); + * // => 0 + * + * // The `_.property` iteratee shorthand. + * _.findIndex(users, 'active'); + * // => 2 + */ + function findIndex(array, predicate, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = nativeMax(length + index, 0); + } + return baseFindIndex(array, getIteratee(predicate, 3), index); + } + + /** + * This method is like `_.findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; }); + * // => 2 + * + * // The `_.matches` iteratee shorthand. + * _.findLastIndex(users, { 'user': 'barney', 'active': true }); + * // => 0 + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastIndex(users, ['active', false]); + * // => 2 + * + * // The `_.property` iteratee shorthand. + * _.findLastIndex(users, 'active'); + * // => 0 + */ + function findLastIndex(array, predicate, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = length - 1; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = fromIndex < 0 + ? nativeMax(length + index, 0) + : nativeMin(index, length - 1); + } + return baseFindIndex(array, getIteratee(predicate, 3), index, true); + } + + /** + * Flattens `array` a single level deep. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flatten([1, [2, [3, [4]], 5]]); + * // => [1, 2, [3, [4]], 5] + */ + function flatten(array) { + var length = array == null ? 0 : array.length; + return length ? baseFlatten(array, 1) : []; + } + + /** + * Recursively flattens `array`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDeep([1, [2, [3, [4]], 5]]); + * // => [1, 2, 3, 4, 5] + */ + function flattenDeep(array) { + var length = array == null ? 0 : array.length; + return length ? baseFlatten(array, INFINITY) : []; + } + + /** + * Recursively flatten `array` up to `depth` times. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Array + * @param {Array} array The array to flatten. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * var array = [1, [2, [3, [4]], 5]]; + * + * _.flattenDepth(array, 1); + * // => [1, 2, [3, [4]], 5] + * + * _.flattenDepth(array, 2); + * // => [1, 2, 3, [4], 5] + */ + function flattenDepth(array, depth) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + depth = depth === undefined ? 1 : toInteger(depth); + return baseFlatten(array, depth); + } + + /** + * The inverse of `_.toPairs`; this method returns an object composed + * from key-value `pairs`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} pairs The key-value pairs. + * @returns {Object} Returns the new object. + * @example + * + * _.fromPairs([['a', 1], ['b', 2]]); + * // => { 'a': 1, 'b': 2 } + */ + function fromPairs(pairs) { + var index = -1, + length = pairs == null ? 0 : pairs.length, + result = {}; + + while (++index < length) { + var pair = pairs[index]; + result[pair[0]] = pair[1]; + } + return result; + } + + /** + * Gets the first element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias first + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @example + * + * _.head([1, 2, 3]); + * // => 1 + * + * _.head([]); + * // => undefined + */ + function head(array) { + return (array && array.length) ? array[0] : undefined; + } + + /** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it's used as the + * offset from the end of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.indexOf([1, 2, 1, 2], 2); + * // => 1 + * + * // Search from the `fromIndex`. + * _.indexOf([1, 2, 1, 2], 2, 2); + * // => 3 + */ + function indexOf(array, value, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = nativeMax(length + index, 0); + } + return baseIndexOf(array, value, index); + } + + /** + * Gets all but the last element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.initial([1, 2, 3]); + * // => [1, 2] + */ + function initial(array) { + var length = array == null ? 0 : array.length; + return length ? baseSlice(array, 0, -1) : []; + } + + /** + * Creates an array of unique values that are included in all given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * _.intersection([2, 1], [2, 3]); + * // => [2] + */ + var intersection = baseRest(function(arrays) { + var mapped = arrayMap(arrays, castArrayLikeObject); + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped) + : []; + }); + + /** + * This method is like `_.intersection` except that it accepts `iteratee` + * which is invoked for each element of each `arrays` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [2.1] + * + * // The `_.property` iteratee shorthand. + * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }] + */ + var intersectionBy = baseRest(function(arrays) { + var iteratee = last(arrays), + mapped = arrayMap(arrays, castArrayLikeObject); + + if (iteratee === last(mapped)) { + iteratee = undefined; + } else { + mapped.pop(); + } + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped, getIteratee(iteratee, 2)) + : []; + }); + + /** + * This method is like `_.intersection` except that it accepts `comparator` + * which is invoked to compare elements of `arrays`. The order and references + * of result values are determined by the first array. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.intersectionWith(objects, others, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }] + */ + var intersectionWith = baseRest(function(arrays) { + var comparator = last(arrays), + mapped = arrayMap(arrays, castArrayLikeObject); + + comparator = typeof comparator == 'function' ? comparator : undefined; + if (comparator) { + mapped.pop(); + } + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped, undefined, comparator) + : []; + }); + + /** + * Converts all elements in `array` into a string separated by `separator`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to convert. + * @param {string} [separator=','] The element separator. + * @returns {string} Returns the joined string. + * @example + * + * _.join(['a', 'b', 'c'], '~'); + * // => 'a~b~c' + */ + function join(array, separator) { + return array == null ? '' : nativeJoin.call(array, separator); + } + + /** + * Gets the last element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the last element of `array`. + * @example + * + * _.last([1, 2, 3]); + * // => 3 + */ + function last(array) { + var length = array == null ? 0 : array.length; + return length ? array[length - 1] : undefined; + } + + /** + * This method is like `_.indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.lastIndexOf([1, 2, 1, 2], 2); + * // => 3 + * + * // Search from the `fromIndex`. + * _.lastIndexOf([1, 2, 1, 2], 2, 2); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = length; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); + } + return value === value + ? strictLastIndexOf(array, value, index) + : baseFindIndex(array, baseIsNaN, index, true); + } + + /** + * Gets the element at index `n` of `array`. If `n` is negative, the nth + * element from the end is returned. + * + * @static + * @memberOf _ + * @since 4.11.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=0] The index of the element to return. + * @returns {*} Returns the nth element of `array`. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * + * _.nth(array, 1); + * // => 'b' + * + * _.nth(array, -2); + * // => 'c'; + */ + function nth(array, n) { + return (array && array.length) ? baseNth(array, toInteger(n)) : undefined; + } + + /** + * Removes all given values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove` + * to remove elements from an array by predicate. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = ['a', 'b', 'c', 'a', 'b', 'c']; + * + * _.pull(array, 'a', 'c'); + * console.log(array); + * // => ['b', 'b'] + */ + var pull = baseRest(pullAll); + + /** + * This method is like `_.pull` except that it accepts an array of values to remove. + * + * **Note:** Unlike `_.difference`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = ['a', 'b', 'c', 'a', 'b', 'c']; + * + * _.pullAll(array, ['a', 'c']); + * console.log(array); + * // => ['b', 'b'] + */ + function pullAll(array, values) { + return (array && array.length && values && values.length) + ? basePullAll(array, values) + : array; + } + + /** + * This method is like `_.pullAll` except that it accepts `iteratee` which is + * invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The iteratee is invoked with one argument: (value). + * + * **Note:** Unlike `_.differenceBy`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns `array`. + * @example + * + * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; + * + * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x'); + * console.log(array); + * // => [{ 'x': 2 }] + */ + function pullAllBy(array, values, iteratee) { + return (array && array.length && values && values.length) + ? basePullAll(array, values, getIteratee(iteratee, 2)) + : array; + } + + /** + * This method is like `_.pullAll` except that it accepts `comparator` which + * is invoked to compare elements of `array` to `values`. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `_.differenceWith`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + * @example + * + * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }]; + * + * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual); + * console.log(array); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] + */ + function pullAllWith(array, values, comparator) { + return (array && array.length && values && values.length) + ? basePullAll(array, values, undefined, comparator) + : array; + } + + /** + * Removes elements from `array` corresponding to `indexes` and returns an + * array of removed elements. + * + * **Note:** Unlike `_.at`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * var pulled = _.pullAt(array, [1, 3]); + * + * console.log(array); + * // => ['a', 'c'] + * + * console.log(pulled); + * // => ['b', 'd'] + */ + var pullAt = flatRest(function(array, indexes) { + var length = array == null ? 0 : array.length, + result = baseAt(array, indexes); + + basePullAt(array, arrayMap(indexes, function(index) { + return isIndex(index, length) ? +index : index; + }).sort(compareAscending)); + + return result; + }); + + /** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is invoked + * with three arguments: (value, index, array). + * + * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull` + * to pull elements from an array by value. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4]; + * var evens = _.remove(array, function(n) { + * return n % 2 == 0; + * }); + * + * console.log(array); + * // => [1, 3] + * + * console.log(evens); + * // => [2, 4] + */ + function remove(array, predicate) { + var result = []; + if (!(array && array.length)) { + return result; + } + var index = -1, + indexes = [], + length = array.length; + + predicate = getIteratee(predicate, 3); + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result.push(value); + indexes.push(index); + } + } + basePullAt(array, indexes); + return result; + } + + /** + * Reverses `array` so that the first element becomes the last, the second + * element becomes the second to last, and so on. + * + * **Note:** This method mutates `array` and is based on + * [`Array#reverse`](https://mdn.io/Array/reverse). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.reverse(array); + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function reverse(array) { + return array == null ? array : nativeReverse.call(array); + } + + /** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of + * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are + * returned. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function slice(array, start, end) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { + start = 0; + end = length; + } + else { + start = start == null ? 0 : toInteger(start); + end = end === undefined ? length : toInteger(end); + } + return baseSlice(array, start, end); + } + + /** + * Uses a binary search to determine the lowest index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedIndex([30, 50], 40); + * // => 1 + */ + function sortedIndex(array, value) { + return baseSortedIndex(array, value); + } + + /** + * This method is like `_.sortedIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * var objects = [{ 'x': 4 }, { 'x': 5 }]; + * + * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); + * // => 0 + * + * // The `_.property` iteratee shorthand. + * _.sortedIndexBy(objects, { 'x': 4 }, 'x'); + * // => 0 + */ + function sortedIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, getIteratee(iteratee, 2)); + } + + /** + * This method is like `_.indexOf` except that it performs a binary + * search on a sorted `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.sortedIndexOf([4, 5, 5, 5, 6], 5); + * // => 1 + */ + function sortedIndexOf(array, value) { + var length = array == null ? 0 : array.length; + if (length) { + var index = baseSortedIndex(array, value); + if (index < length && eq(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * This method is like `_.sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedLastIndex([4, 5, 5, 5, 6], 5); + * // => 4 + */ + function sortedLastIndex(array, value) { + return baseSortedIndex(array, value, true); + } + + /** + * This method is like `_.sortedLastIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * var objects = [{ 'x': 4 }, { 'x': 5 }]; + * + * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); + * // => 1 + * + * // The `_.property` iteratee shorthand. + * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x'); + * // => 1 + */ + function sortedLastIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true); + } + + /** + * This method is like `_.lastIndexOf` except that it performs a binary + * search on a sorted `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5); + * // => 3 + */ + function sortedLastIndexOf(array, value) { + var length = array == null ? 0 : array.length; + if (length) { + var index = baseSortedIndex(array, value, true) - 1; + if (eq(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * This method is like `_.uniq` except that it's designed and optimized + * for sorted arrays. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.sortedUniq([1, 1, 2]); + * // => [1, 2] + */ + function sortedUniq(array) { + return (array && array.length) + ? baseSortedUniq(array) + : []; + } + + /** + * This method is like `_.uniqBy` except that it's designed and optimized + * for sorted arrays. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor); + * // => [1.1, 2.3] + */ + function sortedUniqBy(array, iteratee) { + return (array && array.length) + ? baseSortedUniq(array, getIteratee(iteratee, 2)) + : []; + } + + /** + * Gets all but the first element of `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.tail([1, 2, 3]); + * // => [2, 3] + */ + function tail(array) { + var length = array == null ? 0 : array.length; + return length ? baseSlice(array, 1, length) : []; + } + + /** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.take([1, 2, 3]); + * // => [1] + * + * _.take([1, 2, 3], 2); + * // => [1, 2] + * + * _.take([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.take([1, 2, 3], 0); + * // => [] + */ + function take(array, n, guard) { + if (!(array && array.length)) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRight([1, 2, 3]); + * // => [3] + * + * _.takeRight([1, 2, 3], 2); + * // => [2, 3] + * + * _.takeRight([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.takeRight([1, 2, 3], 0); + * // => [] + */ + function takeRight(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + n = length - n; + return baseSlice(array, n < 0 ? 0 : n, length); + } + + /** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.takeRightWhile(users, function(o) { return !o.active; }); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.matches` iteratee shorthand. + * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false }); + * // => objects for ['pebbles'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.takeRightWhile(users, ['active', false]); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.property` iteratee shorthand. + * _.takeRightWhile(users, 'active'); + * // => [] + */ + function takeRightWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), false, true) + : []; + } + + /** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.takeWhile(users, function(o) { return !o.active; }); + * // => objects for ['barney', 'fred'] + * + * // The `_.matches` iteratee shorthand. + * _.takeWhile(users, { 'user': 'barney', 'active': false }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.takeWhile(users, ['active', false]); + * // => objects for ['barney', 'fred'] + * + * // The `_.property` iteratee shorthand. + * _.takeWhile(users, 'active'); + * // => [] + */ + function takeWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3)) + : []; + } + + /** + * Creates an array of unique values, in order, from all given arrays using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.union([2], [1, 2]); + * // => [2, 1] + */ + var union = baseRest(function(arrays) { + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)); + }); + + /** + * This method is like `_.union` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which uniqueness is computed. Result values are chosen from the first + * array in which the value occurs. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.unionBy([2.1], [1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + var unionBy = baseRest(function(arrays) { + var iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)); + }); + + /** + * This method is like `_.union` except that it accepts `comparator` which + * is invoked to compare elements of `arrays`. Result values are chosen from + * the first array in which the value occurs. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of combined values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.unionWith(objects, others, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ + var unionWith = baseRest(function(arrays) { + var comparator = last(arrays); + comparator = typeof comparator == 'function' ? comparator : undefined; + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator); + }); + + /** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurrence of each element + * is kept. The order of result values is determined by the order they occur + * in the array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniq([2, 1, 2]); + * // => [2, 1] + */ + function uniq(array) { + return (array && array.length) ? baseUniq(array) : []; + } + + /** + * This method is like `_.uniq` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * uniqueness is computed. The order of result values is determined by the + * order they occur in the array. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniqBy([2.1, 1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + function uniqBy(array, iteratee) { + return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : []; + } + + /** + * This method is like `_.uniq` except that it accepts `comparator` which + * is invoked to compare elements of `array`. The order of result values is + * determined by the order they occur in the array.The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.uniqWith(objects, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + */ + function uniqWith(array, comparator) { + comparator = typeof comparator == 'function' ? comparator : undefined; + return (array && array.length) ? baseUniq(array, undefined, comparator) : []; + } + + /** + * This method is like `_.zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @static + * @memberOf _ + * @since 1.2.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]); + * // => [['a', 1, true], ['b', 2, false]] + * + * _.unzip(zipped); + * // => [['a', 'b'], [1, 2], [true, false]] + */ + function unzip(array) { + if (!(array && array.length)) { + return []; + } + var length = 0; + array = arrayFilter(array, function(group) { + if (isArrayLikeObject(group)) { + length = nativeMax(group.length, length); + return true; + } + }); + return baseTimes(length, function(index) { + return arrayMap(array, baseProperty(index)); + }); + } + + /** + * This method is like `_.unzip` except that it accepts `iteratee` to specify + * how regrouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @param {Function} [iteratee=_.identity] The function to combine + * regrouped values. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip([1, 2], [10, 20], [100, 200]); + * // => [[1, 10, 100], [2, 20, 200]] + * + * _.unzipWith(zipped, _.add); + * // => [3, 30, 300] + */ + function unzipWith(array, iteratee) { + if (!(array && array.length)) { + return []; + } + var result = unzip(array); + if (iteratee == null) { + return result; + } + return arrayMap(result, function(group) { + return apply(iteratee, undefined, group); + }); + } + + /** + * Creates an array excluding all given values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.pull`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see _.difference, _.xor + * @example + * + * _.without([2, 1, 2, 3], 1, 2); + * // => [3] + */ + var without = baseRest(function(array, values) { + return isArrayLikeObject(array) + ? baseDifference(array, values) + : []; + }); + + /** + * Creates an array of unique values that is the + * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the given arrays. The order of result values is determined by the order + * they occur in the arrays. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of filtered values. + * @see _.difference, _.without + * @example + * + * _.xor([2, 1], [2, 3]); + * // => [1, 3] + */ + var xor = baseRest(function(arrays) { + return baseXor(arrayFilter(arrays, isArrayLikeObject)); + }); + + /** + * This method is like `_.xor` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which by which they're compared. The order of result values is determined + * by the order they occur in the arrays. The iteratee is invoked with one + * argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [1.2, 3.4] + * + * // The `_.property` iteratee shorthand. + * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 2 }] + */ + var xorBy = baseRest(function(arrays) { + var iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2)); + }); + + /** + * This method is like `_.xor` except that it accepts `comparator` which is + * invoked to compare elements of `arrays`. The order of result values is + * determined by the order they occur in the arrays. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.xorWith(objects, others, _.isEqual); + * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ + var xorWith = baseRest(function(arrays) { + var comparator = last(arrays); + comparator = typeof comparator == 'function' ? comparator : undefined; + return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator); + }); + + /** + * Creates an array of grouped elements, the first of which contains the + * first elements of the given arrays, the second of which contains the + * second elements of the given arrays, and so on. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zip(['a', 'b'], [1, 2], [true, false]); + * // => [['a', 1, true], ['b', 2, false]] + */ + var zip = baseRest(unzip); + + /** + * This method is like `_.fromPairs` except that it accepts two arrays, + * one of property identifiers and one of corresponding values. + * + * @static + * @memberOf _ + * @since 0.4.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObject(['a', 'b'], [1, 2]); + * // => { 'a': 1, 'b': 2 } + */ + function zipObject(props, values) { + return baseZipObject(props || [], values || [], assignValue); + } + + /** + * This method is like `_.zipObject` except that it supports property paths. + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]); + * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } + */ + function zipObjectDeep(props, values) { + return baseZipObject(props || [], values || [], baseSet); + } + + /** + * This method is like `_.zip` except that it accepts `iteratee` to specify + * how grouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @param {Function} [iteratee=_.identity] The function to combine + * grouped values. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) { + * return a + b + c; + * }); + * // => [111, 222] + */ + var zipWith = baseRest(function(arrays) { + var length = arrays.length, + iteratee = length > 1 ? arrays[length - 1] : undefined; + + iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined; + return unzipWith(arrays, iteratee); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` wrapper instance that wraps `value` with explicit method + * chain sequences enabled. The result of such sequences must be unwrapped + * with `_#value`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Seq + * @param {*} value The value to wrap. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'pebbles', 'age': 1 } + * ]; + * + * var youngest = _ + * .chain(users) + * .sortBy('age') + * .map(function(o) { + * return o.user + ' is ' + o.age; + * }) + * .head() + * .value(); + * // => 'pebbles is 1' + */ + function chain(value) { + var result = lodash(value); + result.__chain__ = true; + return result; + } + + /** + * This method invokes `interceptor` and returns `value`. The interceptor + * is invoked with one argument; (value). The purpose of this method is to + * "tap into" a method chain sequence in order to modify intermediate results. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {*} Returns `value`. + * @example + * + * _([1, 2, 3]) + * .tap(function(array) { + * // Mutate input array. + * array.pop(); + * }) + * .reverse() + * .value(); + * // => [2, 1] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * This method is like `_.tap` except that it returns the result of `interceptor`. + * The purpose of this method is to "pass thru" values replacing intermediate + * results in a method chain sequence. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Seq + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {*} Returns the result of `interceptor`. + * @example + * + * _(' abc ') + * .chain() + * .trim() + * .thru(function(value) { + * return [value]; + * }) + * .value(); + * // => ['abc'] + */ + function thru(value, interceptor) { + return interceptor(value); + } + + /** + * This method is the wrapper version of `_.at`. + * + * @name at + * @memberOf _ + * @since 1.0.0 + * @category Seq + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _(object).at(['a[0].b.c', 'a[1]']).value(); + * // => [3, 4] + */ + var wrapperAt = flatRest(function(paths) { + var length = paths.length, + start = length ? paths[0] : 0, + value = this.__wrapped__, + interceptor = function(object) { return baseAt(object, paths); }; + + if (length > 1 || this.__actions__.length || + !(value instanceof LazyWrapper) || !isIndex(start)) { + return this.thru(interceptor); + } + value = value.slice(start, +start + (length ? 1 : 0)); + value.__actions__.push({ + 'func': thru, + 'args': [interceptor], + 'thisArg': undefined + }); + return new LodashWrapper(value, this.__chain__).thru(function(array) { + if (length && !array.length) { + array.push(undefined); + } + return array; + }); + }); + + /** + * Creates a `lodash` wrapper instance with explicit method chain sequences enabled. + * + * @name chain + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // A sequence without explicit chaining. + * _(users).head(); + * // => { 'user': 'barney', 'age': 36 } + * + * // A sequence with explicit chaining. + * _(users) + * .chain() + * .head() + * .pick('user') + * .value(); + * // => { 'user': 'barney' } + */ + function wrapperChain() { + return chain(this); + } + + /** + * Executes the chain sequence and returns the wrapped result. + * + * @name commit + * @memberOf _ + * @since 3.2.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).push(3); + * + * console.log(array); + * // => [1, 2] + * + * wrapped = wrapped.commit(); + * console.log(array); + * // => [1, 2, 3] + * + * wrapped.last(); + * // => 3 + * + * console.log(array); + * // => [1, 2, 3] + */ + function wrapperCommit() { + return new LodashWrapper(this.value(), this.__chain__); + } + + /** + * Gets the next value on a wrapped object following the + * [iterator protocol](https://mdn.io/iteration_protocols#iterator). + * + * @name next + * @memberOf _ + * @since 4.0.0 + * @category Seq + * @returns {Object} Returns the next iterator value. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped.next(); + * // => { 'done': false, 'value': 1 } + * + * wrapped.next(); + * // => { 'done': false, 'value': 2 } + * + * wrapped.next(); + * // => { 'done': true, 'value': undefined } + */ + function wrapperNext() { + if (this.__values__ === undefined) { + this.__values__ = toArray(this.value()); + } + var done = this.__index__ >= this.__values__.length, + value = done ? undefined : this.__values__[this.__index__++]; + + return { 'done': done, 'value': value }; + } + + /** + * Enables the wrapper to be iterable. + * + * @name Symbol.iterator + * @memberOf _ + * @since 4.0.0 + * @category Seq + * @returns {Object} Returns the wrapper object. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped[Symbol.iterator]() === wrapped; + * // => true + * + * Array.from(wrapped); + * // => [1, 2] + */ + function wrapperToIterator() { + return this; + } + + /** + * Creates a clone of the chain sequence planting `value` as the wrapped value. + * + * @name plant + * @memberOf _ + * @since 3.2.0 + * @category Seq + * @param {*} value The value to plant. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2]).map(square); + * var other = wrapped.plant([3, 4]); + * + * other.value(); + * // => [9, 16] + * + * wrapped.value(); + * // => [1, 4] + */ + function wrapperPlant(value) { + var result, + parent = this; + + while (parent instanceof baseLodash) { + var clone = wrapperClone(parent); + clone.__index__ = 0; + clone.__values__ = undefined; + if (result) { + previous.__wrapped__ = clone; + } else { + result = clone; + } + var previous = clone; + parent = parent.__wrapped__; + } + previous.__wrapped__ = value; + return result; + } + + /** + * This method is the wrapper version of `_.reverse`. + * + * **Note:** This method mutates the wrapped array. + * + * @name reverse + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2, 3]; + * + * _(array).reverse().value() + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function wrapperReverse() { + var value = this.__wrapped__; + if (value instanceof LazyWrapper) { + var wrapped = value; + if (this.__actions__.length) { + wrapped = new LazyWrapper(this); + } + wrapped = wrapped.reverse(); + wrapped.__actions__.push({ + 'func': thru, + 'args': [reverse], + 'thisArg': undefined + }); + return new LodashWrapper(wrapped, this.__chain__); + } + return this.thru(reverse); + } + + /** + * Executes the chain sequence to resolve the unwrapped value. + * + * @name value + * @memberOf _ + * @since 0.1.0 + * @alias toJSON, valueOf + * @category Seq + * @returns {*} Returns the resolved unwrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return baseWrapperValue(this.__wrapped__, this.__actions__); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the number of times the key was returned by `iteratee`. The + * iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([6.1, 4.2, 6.3], Math.floor); + * // => { '4': 1, '6': 2 } + * + * // The `_.property` iteratee shorthand. + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */ + var countBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + ++result[key]; + } else { + baseAssignValue(result, key, 1); + } + }); + + /** + * Checks if `predicate` returns truthy for **all** elements of `collection`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index|key, collection). + * + * **Note:** This method returns `true` for + * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because + * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of + * elements of empty collections. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.every(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // The `_.matchesProperty` iteratee shorthand. + * _.every(users, ['active', false]); + * // => true + * + * // The `_.property` iteratee shorthand. + * _.every(users, 'active'); + * // => false + */ + function every(collection, predicate, guard) { + var func = isArray(collection) ? arrayEvery : baseEvery; + if (guard && isIterateeCall(collection, predicate, guard)) { + predicate = undefined; + } + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Iterates over elements of `collection`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index|key, collection). + * + * **Note:** Unlike `_.remove`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see _.reject + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.filter(users, function(o) { return !o.active; }); + * // => objects for ['fred'] + * + * // The `_.matches` iteratee shorthand. + * _.filter(users, { 'age': 36, 'active': true }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.filter(users, ['active', false]); + * // => objects for ['fred'] + * + * // The `_.property` iteratee shorthand. + * _.filter(users, 'active'); + * // => objects for ['barney'] + * + * // Combining several predicates using `_.overEvery` or `_.overSome`. + * _.filter(users, _.overSome([{ 'age': 36 }, ['age', 40]])); + * // => objects for ['fred', 'barney'] + */ + function filter(collection, predicate) { + var func = isArray(collection) ? arrayFilter : baseFilter; + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Iterates over elements of `collection`, returning the first element + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=0] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false }, + * { 'user': 'pebbles', 'age': 1, 'active': true } + * ]; + * + * _.find(users, function(o) { return o.age < 40; }); + * // => object for 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.find(users, { 'age': 1, 'active': true }); + * // => object for 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.find(users, ['active', false]); + * // => object for 'fred' + * + * // The `_.property` iteratee shorthand. + * _.find(users, 'active'); + * // => object for 'barney' + */ + var find = createFind(findIndex); + + /** + * This method is like `_.find` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=collection.length-1] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(n) { + * return n % 2 == 1; + * }); + * // => 3 + */ + var findLast = createFind(findLastIndex); + + /** + * Creates a flattened array of values by running each element in `collection` + * thru `iteratee` and flattening the mapped results. The iteratee is invoked + * with three arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [n, n]; + * } + * + * _.flatMap([1, 2], duplicate); + * // => [1, 1, 2, 2] + */ + function flatMap(collection, iteratee) { + return baseFlatten(map(collection, iteratee), 1); + } + + /** + * This method is like `_.flatMap` except that it recursively flattens the + * mapped results. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [[[n, n]]]; + * } + * + * _.flatMapDeep([1, 2], duplicate); + * // => [1, 1, 2, 2] + */ + function flatMapDeep(collection, iteratee) { + return baseFlatten(map(collection, iteratee), INFINITY); + } + + /** + * This method is like `_.flatMap` except that it recursively flattens the + * mapped results up to `depth` times. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [[[n, n]]]; + * } + * + * _.flatMapDepth([1, 2], duplicate, 2); + * // => [[1, 1], [2, 2]] + */ + function flatMapDepth(collection, iteratee, depth) { + depth = depth === undefined ? 1 : toInteger(depth); + return baseFlatten(map(collection, iteratee), depth); + } + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" + * property are iterated like arrays. To avoid this behavior use `_.forIn` + * or `_.forOwn` for object iteration. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias each + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEachRight + * @example + * + * _.forEach([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `1` then `2`. + * + * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forEach(collection, iteratee) { + var func = isArray(collection) ? arrayEach : baseEach; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @alias eachRight + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEach + * @example + * + * _.forEachRight([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `2` then `1`. + */ + function forEachRight(collection, iteratee) { + var func = isArray(collection) ? arrayEachRight : baseEachRight; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The order of grouped values + * is determined by the order they occur in `collection`. The corresponding + * value of each key is an array of elements responsible for generating the + * key. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([6.1, 4.2, 6.3], Math.floor); + * // => { '4': [4.2], '6': [6.1, 6.3] } + * + * // The `_.property` iteratee shorthand. + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + result[key].push(value); + } else { + baseAssignValue(result, key, [value]); + } + }); + + /** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * is used for equality comparisons. If `fromIndex` is negative, it's used as + * the offset from the end of `collection`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ + function includes(collection, value, fromIndex, guard) { + collection = isArrayLike(collection) ? collection : values(collection); + fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0; + + var length = collection.length; + if (fromIndex < 0) { + fromIndex = nativeMax(length + fromIndex, 0); + } + return isString(collection) + ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1) + : (!!length && baseIndexOf(collection, value, fromIndex) > -1); + } + + /** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `path` is a function, it's invoked + * for, and `this` bound to, each element in `collection`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {...*} [args] The arguments to invoke each method with. + * @returns {Array} Returns the array of results. + * @example + * + * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invokeMap([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invokeMap = baseRest(function(collection, path, args) { + var index = -1, + isFunc = typeof path == 'function', + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value) { + result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args); + }); + return result; + }); + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the last element responsible for generating the key. The + * iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * var array = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ]; + * + * _.keyBy(array, function(o) { + * return String.fromCharCode(o.code); + * }); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + * + * _.keyBy(array, 'dir'); + * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } + */ + var keyBy = createAggregator(function(result, value, key) { + baseAssignValue(result, key, value); + }); + + /** + * Creates an array of values by running each element in `collection` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. + * + * The guarded methods are: + * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`, + * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`, + * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`, + * `template`, `trim`, `trimEnd`, `trimStart`, and `words` + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n; + * } + * + * _.map([4, 8], square); + * // => [16, 64] + * + * _.map({ 'a': 4, 'b': 8 }, square); + * // => [16, 64] (iteration order is not guaranteed) + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * // The `_.property` iteratee shorthand. + * _.map(users, 'user'); + * // => ['barney', 'fred'] + */ + function map(collection, iteratee) { + var func = isArray(collection) ? arrayMap : baseMap; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.sortBy` except that it allows specifying the sort + * orders of the iteratees to sort by. If `orders` is unspecified, all values + * are sorted in ascending order. Otherwise, specify an order of "desc" for + * descending or "asc" for ascending sort order of corresponding values. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]] + * The iteratees to sort by. + * @param {string[]} [orders] The sort orders of `iteratees`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 36 } + * ]; + * + * // Sort by `user` in ascending order and by `age` in descending order. + * _.orderBy(users, ['user', 'age'], ['asc', 'desc']); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + */ + function orderBy(collection, iteratees, orders, guard) { + if (collection == null) { + return []; + } + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + orders = guard ? undefined : orders; + if (!isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseOrderBy(collection, iteratees, orders); + } + + /** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, the second of which + * contains elements `predicate` returns falsey for. The predicate is + * invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the array of grouped elements. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ]; + * + * _.partition(users, function(o) { return o.active; }); + * // => objects for [['fred'], ['barney', 'pebbles']] + * + * // The `_.matches` iteratee shorthand. + * _.partition(users, { 'age': 1, 'active': false }); + * // => objects for [['pebbles'], ['barney', 'fred']] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.partition(users, ['active', false]); + * // => objects for [['barney', 'pebbles'], ['fred']] + * + * // The `_.property` iteratee shorthand. + * _.partition(users, 'active'); + * // => objects for [['fred'], ['barney', 'pebbles']] + */ + var partition = createAggregator(function(result, value, key) { + result[key ? 0 : 1].push(value); + }, function() { return [[], []]; }); + + /** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `collection` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.reduce`, `_.reduceRight`, and `_.transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, + * and `sortBy` + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see _.reduceRight + * @example + * + * _.reduce([1, 2], function(sum, n) { + * return sum + n; + * }, 0); + * // => 3 + * + * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * return result; + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) + */ + function reduce(collection, iteratee, accumulator) { + var func = isArray(collection) ? arrayReduce : baseReduce, + initAccum = arguments.length < 3; + + return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach); + } + + /** + * This method is like `_.reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see _.reduce + * @example + * + * var array = [[0, 1], [2, 3], [4, 5]]; + * + * _.reduceRight(array, function(flattened, other) { + * return flattened.concat(other); + * }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, iteratee, accumulator) { + var func = isArray(collection) ? arrayReduceRight : baseReduce, + initAccum = arguments.length < 3; + + return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight); + } + + /** + * The opposite of `_.filter`; this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see _.filter + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true } + * ]; + * + * _.reject(users, function(o) { return !o.active; }); + * // => objects for ['fred'] + * + * // The `_.matches` iteratee shorthand. + * _.reject(users, { 'age': 40, 'active': true }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.reject(users, ['active', false]); + * // => objects for ['fred'] + * + * // The `_.property` iteratee shorthand. + * _.reject(users, 'active'); + * // => objects for ['barney'] + */ + function reject(collection, predicate) { + var func = isArray(collection) ? arrayFilter : baseFilter; + return func(collection, negate(getIteratee(predicate, 3))); + } + + /** + * Gets a random element from `collection`. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to sample. + * @returns {*} Returns the random element. + * @example + * + * _.sample([1, 2, 3, 4]); + * // => 2 + */ + function sample(collection) { + var func = isArray(collection) ? arraySample : baseSample; + return func(collection); + } + + /** + * Gets `n` random elements at unique keys from `collection` up to the + * size of `collection`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to sample. + * @param {number} [n=1] The number of elements to sample. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the random elements. + * @example + * + * _.sampleSize([1, 2, 3], 2); + * // => [3, 1] + * + * _.sampleSize([1, 2, 3], 4); + * // => [2, 3, 1] + */ + function sampleSize(collection, n, guard) { + if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) { + n = 1; + } else { + n = toInteger(n); + } + var func = isArray(collection) ? arraySampleSize : baseSampleSize; + return func(collection, n); + } + + /** + * Creates an array of shuffled values, using a version of the + * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4]); + * // => [4, 1, 3, 2] + */ + function shuffle(collection) { + var func = isArray(collection) ? arrayShuffle : baseShuffle; + return func(collection); + } + + /** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable string keyed properties for objects. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * _.size([1, 2, 3]); + * // => 3 + * + * _.size({ 'a': 1, 'b': 2 }); + * // => 2 + * + * _.size('pebbles'); + * // => 7 + */ + function size(collection) { + if (collection == null) { + return 0; + } + if (isArrayLike(collection)) { + return isString(collection) ? stringSize(collection) : collection.length; + } + var tag = getTag(collection); + if (tag == mapTag || tag == setTag) { + return collection.size; + } + return baseKeys(collection).length; + } + + /** + * Checks if `predicate` returns truthy for **any** element of `collection`. + * Iteration is stopped once `predicate` returns truthy. The predicate is + * invoked with three arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.some(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // The `_.matchesProperty` iteratee shorthand. + * _.some(users, ['active', false]); + * // => true + * + * // The `_.property` iteratee shorthand. + * _.some(users, 'active'); + * // => true + */ + function some(collection, predicate, guard) { + var func = isArray(collection) ? arraySome : baseSome; + if (guard && isIterateeCall(collection, predicate, guard)) { + predicate = undefined; + } + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection thru each iteratee. This method + * performs a stable sort, that is, it preserves the original sort order of + * equal elements. The iteratees are invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {...(Function|Function[])} [iteratees=[_.identity]] + * The iteratees to sort by. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 30 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.sortBy(users, [function(o) { return o.user; }]); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]] + * + * _.sortBy(users, ['user', 'age']); + * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]] + */ + var sortBy = baseRest(function(collection, iteratees) { + if (collection == null) { + return []; + } + var length = iteratees.length; + if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { + iteratees = []; + } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { + iteratees = [iteratees[0]]; + } + return baseOrderBy(collection, baseFlatten(iteratees, 1), []); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ + var now = ctxNow || function() { + return root.Date.now(); + }; + + /*------------------------------------------------------------------------*/ + + /** + * The opposite of `_.before`; this method creates a function that invokes + * `func` once it's called `n` or more times. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var saves = ['profile', 'settings']; + * + * var done = _.after(saves.length, function() { + * console.log('done saving!'); + * }); + * + * _.forEach(saves, function(type) { + * asyncSave({ 'type': type, 'complete': done }); + * }); + * // => Logs 'done saving!' after the two async saves have completed. + */ + function after(n, func) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a function that invokes `func`, with up to `n` arguments, + * ignoring any additional arguments. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to cap arguments for. + * @param {number} [n=func.length] The arity cap. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new capped function. + * @example + * + * _.map(['6', '8', '10'], _.ary(parseInt, 1)); + * // => [6, 8, 10] + */ + function ary(func, n, guard) { + n = guard ? undefined : n; + n = (func && n == null) ? func.length : n; + return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n); + } + + /** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it's called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery(element).on('click', _.before(5, addContactToList)); + * // => Allows adding up to 4 contacts to the list. + */ + function before(n, func) { + var result; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; + } + + /** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and `partials` prepended to the arguments it receives. + * + * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for partially applied arguments. + * + * **Note:** Unlike native `Function#bind`, this method doesn't set the "length" + * property of bound functions. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * function greet(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * + * var object = { 'user': 'fred' }; + * + * var bound = _.bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * // Bound with placeholders. + * var bound = _.bind(greet, object, _, '!'); + * bound('hi'); + * // => 'hi fred!' + */ + var bind = baseRest(function(func, thisArg, partials) { + var bitmask = WRAP_BIND_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, getHolder(bind)); + bitmask |= WRAP_PARTIAL_FLAG; + } + return createWrap(func, bitmask, thisArg, partials, holders); + }); + + /** + * Creates a function that invokes the method at `object[key]` with `partials` + * prepended to the arguments it receives. + * + * This method differs from `_.bind` by allowing bound functions to reference + * methods that may be redefined or don't yet exist. See + * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) + * for more details. + * + * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Function + * @param {Object} object The object to invoke the method on. + * @param {string} key The key of the method. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var object = { + * 'user': 'fred', + * 'greet': function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * }; + * + * var bound = _.bindKey(object, 'greet', 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * object.greet = function(greeting, punctuation) { + * return greeting + 'ya ' + this.user + punctuation; + * }; + * + * bound('!'); + * // => 'hiya fred!' + * + * // Bound with placeholders. + * var bound = _.bindKey(object, 'greet', _, '!'); + * bound('hi'); + * // => 'hiya fred!' + */ + var bindKey = baseRest(function(object, key, partials) { + var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, getHolder(bindKey)); + bitmask |= WRAP_PARTIAL_FLAG; + } + return createWrap(key, bitmask, object, partials, holders); + }); + + /** + * Creates a function that accepts arguments of `func` and either invokes + * `func` returning its result, if at least `arity` number of arguments have + * been provided, or returns a function that accepts the remaining `func` + * arguments, and so on. The arity of `func` may be specified if `func.length` + * is not sufficient. + * + * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for provided arguments. + * + * **Note:** This method doesn't set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curry(abc); + * + * curried(1)(2)(3); + * // => [1, 2, 3] + * + * curried(1, 2)(3); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // Curried with placeholders. + * curried(1)(_, 3)(2); + * // => [1, 2, 3] + */ + function curry(func, arity, guard) { + arity = guard ? undefined : arity; + var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curry.placeholder; + return result; + } + + /** + * This method is like `_.curry` except that arguments are applied to `func` + * in the manner of `_.partialRight` instead of `_.partial`. + * + * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for provided arguments. + * + * **Note:** This method doesn't set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curryRight(abc); + * + * curried(3)(2)(1); + * // => [1, 2, 3] + * + * curried(2, 3)(1); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // Curried with placeholders. + * curried(3)(1, _)(2); + * // => [1, 2, 3] + */ + function curryRight(func, arity, guard) { + arity = guard ? undefined : arity; + var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curryRight.placeholder; + return result; + } + + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ + function debounce(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = toNumber(wait) || 0; + if (isObject(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = setTimeout(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + timeWaiting = wait - timeSinceLastCall; + + return maxing + ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) + : timeWaiting; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return (lastCallTime === undefined || (timeSinceLastCall >= wait) || + (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); + } + + function timerExpired() { + var time = now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now()); + } + + function debounced() { + var time = now(), + isInvoking = shouldInvoke(time); + + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + clearTimeout(timerId); + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; + } + + /** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * _.defer(function(text) { + * console.log(text); + * }, 'deferred'); + * // => Logs 'deferred' after one millisecond. + */ + var defer = baseRest(function(func, args) { + return baseDelay(func, 1, args); + }); + + /** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * _.delay(function(text) { + * console.log(text); + * }, 1000, 'later'); + * // => Logs 'later' after one second. + */ + var delay = baseRest(function(func, wait, args) { + return baseDelay(func, toNumber(wait) || 0, args); + }); + + /** + * Creates a function that invokes `func` with arguments reversed. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to flip arguments for. + * @returns {Function} Returns the new flipped function. + * @example + * + * var flipped = _.flip(function() { + * return _.toArray(arguments); + * }); + * + * flipped('a', 'b', 'c', 'd'); + * // => ['d', 'c', 'b', 'a'] + */ + function flip(func) { + return createWrap(func, WRAP_FLIP_FLAG); + } + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `clear`, `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result) || cache; + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + + // Expose `MapCache`. + memoize.Cache = MapCache; + + /** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new negated function. + * @example + * + * function isEven(n) { + * return n % 2 == 0; + * } + * + * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); + * // => [1, 3, 5] + */ + function negate(predicate) { + if (typeof predicate != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function() { + var args = arguments; + switch (args.length) { + case 0: return !predicate.call(this); + case 1: return !predicate.call(this, args[0]); + case 2: return !predicate.call(this, args[0], args[1]); + case 3: return !predicate.call(this, args[0], args[1], args[2]); + } + return !predicate.apply(this, args); + }; + } + + /** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first invocation. The `func` is + * invoked with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // => `createApplication` is invoked once + */ + function once(func) { + return before(2, func); + } + + /** + * Creates a function that invokes `func` with its arguments transformed. + * + * @static + * @since 4.0.0 + * @memberOf _ + * @category Function + * @param {Function} func The function to wrap. + * @param {...(Function|Function[])} [transforms=[_.identity]] + * The argument transforms. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2; + * } + * + * function square(n) { + * return n * n; + * } + * + * var func = _.overArgs(function(x, y) { + * return [x, y]; + * }, [square, doubled]); + * + * func(9, 3); + * // => [81, 6] + * + * func(10, 5); + * // => [100, 10] + */ + var overArgs = castRest(function(func, transforms) { + transforms = (transforms.length == 1 && isArray(transforms[0])) + ? arrayMap(transforms[0], baseUnary(getIteratee())) + : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee())); + + var funcsLength = transforms.length; + return baseRest(function(args) { + var index = -1, + length = nativeMin(args.length, funcsLength); + + while (++index < length) { + args[index] = transforms[index].call(this, args[index]); + } + return apply(func, this, args); + }); + }); + + /** + * Creates a function that invokes `func` with `partials` prepended to the + * arguments it receives. This method is like `_.bind` except it does **not** + * alter the `this` binding. + * + * The `_.partial.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method doesn't set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @since 0.2.0 + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * function greet(greeting, name) { + * return greeting + ' ' + name; + * } + * + * var sayHelloTo = _.partial(greet, 'hello'); + * sayHelloTo('fred'); + * // => 'hello fred' + * + * // Partially applied with placeholders. + * var greetFred = _.partial(greet, _, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + */ + var partial = baseRest(function(func, partials) { + var holders = replaceHolders(partials, getHolder(partial)); + return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders); + }); + + /** + * This method is like `_.partial` except that partially applied arguments + * are appended to the arguments it receives. + * + * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method doesn't set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * function greet(greeting, name) { + * return greeting + ' ' + name; + * } + * + * var greetFred = _.partialRight(greet, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + * + * // Partially applied with placeholders. + * var sayHelloTo = _.partialRight(greet, 'hello', _); + * sayHelloTo('fred'); + * // => 'hello fred' + */ + var partialRight = baseRest(function(func, partials) { + var holders = replaceHolders(partials, getHolder(partialRight)); + return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders); + }); + + /** + * Creates a function that invokes `func` with arguments arranged according + * to the specified `indexes` where the argument value at the first index is + * provided as the first argument, the argument value at the second index is + * provided as the second argument, and so on. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to rearrange arguments for. + * @param {...(number|number[])} indexes The arranged argument indexes. + * @returns {Function} Returns the new function. + * @example + * + * var rearged = _.rearg(function(a, b, c) { + * return [a, b, c]; + * }, [2, 0, 1]); + * + * rearged('b', 'c', 'a') + * // => ['a', 'b', 'c'] + */ + var rearg = flatRest(function(func, indexes) { + return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes); + }); + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as + * an array. + * + * **Note:** This method is based on the + * [rest parameter](https://mdn.io/rest_parameters). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.rest(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function rest(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = start === undefined ? start : toInteger(start); + return baseRest(func, start); + } + + /** + * Creates a function that invokes `func` with the `this` binding of the + * create function and an array of arguments much like + * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply). + * + * **Note:** This method is based on the + * [spread operator](https://mdn.io/spread_operator). + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Function + * @param {Function} func The function to spread arguments over. + * @param {number} [start=0] The start position of the spread. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.spread(function(who, what) { + * return who + ' says ' + what; + * }); + * + * say(['fred', 'hello']); + * // => 'fred says hello' + * + * var numbers = Promise.all([ + * Promise.resolve(40), + * Promise.resolve(36) + * ]); + * + * numbers.then(_.spread(function(x, y) { + * return x + y; + * })); + * // => a Promise of 76 + */ + function spread(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = start == null ? 0 : nativeMax(toInteger(start), 0); + return baseRest(function(args) { + var array = args[start], + otherArgs = castSlice(args, 0, start); + + if (array) { + arrayPush(otherArgs, array); + } + return apply(func, this, otherArgs); + }); + } + + /** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed `func` invocations and a `flush` method to + * immediately invoke them. Provide `options` to indicate whether `func` + * should be invoked on the leading and/or trailing edge of the `wait` + * timeout. The `func` is invoked with the last arguments provided to the + * throttled function. Subsequent calls to the throttled function return the + * result of the last `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the throttled function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=true] + * Specify invoking on the leading edge of the timeout. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // Avoid excessively updating the position while scrolling. + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. + * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); + * jQuery(element).on('click', throttled); + * + * // Cancel the trailing throttled invocation. + * jQuery(window).on('popstate', throttled.cancel); + */ + function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { + 'leading': leading, + 'maxWait': wait, + 'trailing': trailing + }); + } + + /** + * Creates a function that accepts up to one argument, ignoring any + * additional arguments. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + * @example + * + * _.map(['6', '8', '10'], _.unary(parseInt)); + * // => [6, 8, 10] + */ + function unary(func) { + return ary(func, 1); + } + + /** + * Creates a function that provides `value` to `wrapper` as its first + * argument. Any additional arguments provided to the function are appended + * to those provided to the `wrapper`. The wrapper is invoked with the `this` + * binding of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {*} value The value to wrap. + * @param {Function} [wrapper=identity] The wrapper function. + * @returns {Function} Returns the new function. + * @example + * + * var p = _.wrap(_.escape, function(func, text) { + * return '

' + func(text) + '

'; + * }); + * + * p('fred, barney, & pebbles'); + * // => '

fred, barney, & pebbles

' + */ + function wrap(value, wrapper) { + return partial(castFunction(wrapper), value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Casts `value` as an array if it's not one. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Lang + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast array. + * @example + * + * _.castArray(1); + * // => [1] + * + * _.castArray({ 'a': 1 }); + * // => [{ 'a': 1 }] + * + * _.castArray('abc'); + * // => ['abc'] + * + * _.castArray(null); + * // => [null] + * + * _.castArray(undefined); + * // => [undefined] + * + * _.castArray(); + * // => [] + * + * var array = [1, 2, 3]; + * console.log(_.castArray(array) === array); + * // => true + */ + function castArray() { + if (!arguments.length) { + return []; + } + var value = arguments[0]; + return isArray(value) ? value : [value]; + } + + /** + * Creates a shallow clone of `value`. + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) + * and supports cloning arrays, array buffers, booleans, date objects, maps, + * numbers, `Object` objects, regexes, sets, strings, symbols, and typed + * arrays. The own enumerable properties of `arguments` objects are cloned + * as plain objects. An empty object is returned for uncloneable values such + * as error objects, functions, DOM nodes, and WeakMaps. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to clone. + * @returns {*} Returns the cloned value. + * @see _.cloneDeep + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var shallow = _.clone(objects); + * console.log(shallow[0] === objects[0]); + * // => true + */ + function clone(value) { + return baseClone(value, CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.clone` except that it accepts `customizer` which + * is invoked to produce the cloned value. If `customizer` returns `undefined`, + * cloning is handled by the method instead. The `customizer` is invoked with + * up to four arguments; (value [, index|key, object, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the cloned value. + * @see _.cloneDeepWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(false); + * } + * } + * + * var el = _.cloneWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 0 + */ + function cloneWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * This method is like `_.clone` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @returns {*} Returns the deep cloned value. + * @see _.clone + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var deep = _.cloneDeep(objects); + * console.log(deep[0] === objects[0]); + * // => false + */ + function cloneDeep(value) { + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.cloneWith` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the deep cloned value. + * @see _.cloneWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(true); + * } + * } + * + * var el = _.cloneDeepWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 20 + */ + function cloneDeepWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * Checks if `object` conforms to `source` by invoking the predicate + * properties of `source` with the corresponding property values of `object`. + * + * **Note:** This method is equivalent to `_.conforms` when `source` is + * partially applied. + * + * @static + * @memberOf _ + * @since 4.14.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); + * // => true + * + * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); + * // => false + */ + function conformsTo(object, source) { + return source == null || baseConformsTo(object, source, keys(source)); + } + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is greater than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + * @see _.lt + * @example + * + * _.gt(3, 1); + * // => true + * + * _.gt(3, 3); + * // => false + * + * _.gt(1, 3); + * // => false + */ + var gt = createRelationalOperation(baseGt); + + /** + * Checks if `value` is greater than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to + * `other`, else `false`. + * @see _.lte + * @example + * + * _.gte(3, 1); + * // => true + * + * _.gte(3, 3); + * // => true + * + * _.gte(1, 3); + * // => false + */ + var gte = createRelationalOperation(function(value, other) { + return value >= other; + }); + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { + return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && + !propertyIsEnumerable.call(value, 'callee'); + }; + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is classified as an `ArrayBuffer` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + * @example + * + * _.isArrayBuffer(new ArrayBuffer(2)); + * // => true + * + * _.isArrayBuffer(new Array(2)); + * // => false + */ + var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || + (isObjectLike(value) && baseGetTag(value) == boolTag); + } + + /** + * Checks if `value` is a buffer. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * _.isBuffer(new Buffer(2)); + * // => true + * + * _.isBuffer(new Uint8Array(2)); + * // => false + */ + var isBuffer = nativeIsBuffer || stubFalse; + + /** + * Checks if `value` is classified as a `Date` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + * + * _.isDate('Mon April 23 2012'); + * // => false + */ + var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; + + /** + * Checks if `value` is likely a DOM element. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + * + * _.isElement(''); + * // => false + */ + function isElement(value) { + return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); + } + + /** + * Checks if `value` is an empty object, collection, map, or set. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Array-like values such as `arguments` objects, arrays, buffers, strings, or + * jQuery-like collections are considered empty if they have a `length` of `0`. + * Similarly, maps and sets are considered empty if they have a `size` of `0`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * _.isEmpty(null); + * // => true + * + * _.isEmpty(true); + * // => true + * + * _.isEmpty(1); + * // => true + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({ 'a': 1 }); + * // => false + */ + function isEmpty(value) { + if (value == null) { + return true; + } + if (isArrayLike(value) && + (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || + isBuffer(value) || isTypedArray(value) || isArguments(value))) { + return !value.length; + } + var tag = getTag(value); + if (tag == mapTag || tag == setTag) { + return !value.size; + } + if (isPrototype(value)) { + return !baseKeys(value).length; + } + for (var key in value) { + if (hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + } + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent. + * + * **Note:** This method supports comparing arrays, array buffers, booleans, + * date objects, error objects, maps, numbers, `Object` objects, regexes, + * sets, strings, symbols, and typed arrays. `Object` objects are compared + * by their own, not inherited, enumerable properties. Functions and DOM + * nodes are compared by strict equality, i.e. `===`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.isEqual(object, other); + * // => true + * + * object === other; + * // => false + */ + function isEqual(value, other) { + return baseIsEqual(value, other); + } + + /** + * This method is like `_.isEqual` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with up to + * six arguments: (objValue, othValue [, index|key, object, other, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, othValue) { + * if (isGreeting(objValue) && isGreeting(othValue)) { + * return true; + * } + * } + * + * var array = ['hello', 'goodbye']; + * var other = ['hi', 'goodbye']; + * + * _.isEqualWith(array, other, customizer); + * // => true + */ + function isEqualWith(value, other, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + var result = customizer ? customizer(value, other) : undefined; + return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; + } + + /** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * _.isError(new Error); + * // => true + * + * _.isError(Error); + * // => false + */ + function isError(value) { + if (!isObjectLike(value)) { + return false; + } + var tag = baseGetTag(value); + return tag == errorTag || tag == domExcTag || + (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value)); + } + + /** + * Checks if `value` is a finite primitive number. + * + * **Note:** This method is based on + * [`Number.isFinite`](https://mdn.io/Number/isFinite). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. + * @example + * + * _.isFinite(3); + * // => true + * + * _.isFinite(Number.MIN_VALUE); + * // => true + * + * _.isFinite(Infinity); + * // => false + * + * _.isFinite('3'); + * // => false + */ + function isFinite(value) { + return typeof value == 'number' && nativeIsFinite(value); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + if (!isObject(value)) { + return false; + } + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 9 which returns 'object' for typed arrays and other constructors. + var tag = baseGetTag(value); + return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; + } + + /** + * Checks if `value` is an integer. + * + * **Note:** This method is based on + * [`Number.isInteger`](https://mdn.io/Number/isInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an integer, else `false`. + * @example + * + * _.isInteger(3); + * // => true + * + * _.isInteger(Number.MIN_VALUE); + * // => false + * + * _.isInteger(Infinity); + * // => false + * + * _.isInteger('3'); + * // => false + */ + function isInteger(value) { + return typeof value == 'number' && value == toInteger(value); + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return value != null && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Map` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + * @example + * + * _.isMap(new Map); + * // => true + * + * _.isMap(new WeakMap); + * // => false + */ + var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; + + /** + * Performs a partial deep comparison between `object` and `source` to + * determine if `object` contains equivalent property values. + * + * **Note:** This method is equivalent to `_.matches` when `source` is + * partially applied. + * + * Partial comparisons will match empty array and empty object `source` + * values against any array or object value, respectively. See `_.isEqual` + * for a list of supported value comparisons. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.isMatch(object, { 'b': 2 }); + * // => true + * + * _.isMatch(object, { 'b': 1 }); + * // => false + */ + function isMatch(object, source) { + return object === source || baseIsMatch(object, source, getMatchData(source)); + } + + /** + * This method is like `_.isMatch` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with five + * arguments: (objValue, srcValue, index|key, object, source). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, srcValue) { + * if (isGreeting(objValue) && isGreeting(srcValue)) { + * return true; + * } + * } + * + * var object = { 'greeting': 'hello' }; + * var source = { 'greeting': 'hi' }; + * + * _.isMatchWith(object, source, customizer); + * // => true + */ + function isMatchWith(object, source, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseIsMatch(object, source, getMatchData(source), customizer); + } + + /** + * Checks if `value` is `NaN`. + * + * **Note:** This method is based on + * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as + * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for + * `undefined` and other non-number values. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // An `NaN` primitive is the only value that is not equal to itself. + // Perform the `toStringTag` check first to avoid errors with some + // ActiveX objects in IE. + return isNumber(value) && value != +value; + } + + /** + * Checks if `value` is a pristine native function. + * + * **Note:** This method can't reliably detect native functions in the presence + * of the core-js package because core-js circumvents this kind of detection. + * Despite multiple requests, the core-js maintainer has made it clear: any + * attempt to fix the detection will be obstructed. As a result, we're left + * with little choice but to throw an error. Unfortunately, this also affects + * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), + * which rely on core-js. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (isMaskable(value)) { + throw new Error(CORE_ERROR_TEXT); + } + return baseIsNative(value); + } + + /** + * Checks if `value` is `null`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(void 0); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is `null` or `undefined`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is nullish, else `false`. + * @example + * + * _.isNil(null); + * // => true + * + * _.isNil(void 0); + * // => true + * + * _.isNil(NaN); + * // => false + */ + function isNil(value) { + return value == null; + } + + /** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are + * classified as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a number, else `false`. + * @example + * + * _.isNumber(3); + * // => true + * + * _.isNumber(Number.MIN_VALUE); + * // => true + * + * _.isNumber(Infinity); + * // => true + * + * _.isNumber('3'); + * // => false + */ + function isNumber(value) { + return typeof value == 'number' || + (isObjectLike(value) && baseGetTag(value) == numberTag); + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + if (!isObjectLike(value) || baseGetTag(value) != objectTag) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return typeof Ctor == 'function' && Ctor instanceof Ctor && + funcToString.call(Ctor) == objectCtorString; + } + + /** + * Checks if `value` is classified as a `RegExp` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + * @example + * + * _.isRegExp(/abc/); + * // => true + * + * _.isRegExp('/abc/'); + * // => false + */ + var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; + + /** + * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 + * double precision number which isn't the result of a rounded unsafe integer. + * + * **Note:** This method is based on + * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. + * @example + * + * _.isSafeInteger(3); + * // => true + * + * _.isSafeInteger(Number.MIN_VALUE); + * // => false + * + * _.isSafeInteger(Infinity); + * // => false + * + * _.isSafeInteger('3'); + * // => false + */ + function isSafeInteger(value) { + return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is classified as a `Set` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + * @example + * + * _.isSet(new Set); + * // => true + * + * _.isSet(new WeakSet); + * // => false + */ + var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; + + /** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a string, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ + function isString(value) { + return typeof value == 'string' || + (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag); + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && baseGetTag(value) == symbolTag); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Checks if `value` is `undefined`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + * + * _.isUndefined(null); + * // => false + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Checks if `value` is classified as a `WeakMap` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. + * @example + * + * _.isWeakMap(new WeakMap); + * // => true + * + * _.isWeakMap(new Map); + * // => false + */ + function isWeakMap(value) { + return isObjectLike(value) && getTag(value) == weakMapTag; + } + + /** + * Checks if `value` is classified as a `WeakSet` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. + * @example + * + * _.isWeakSet(new WeakSet); + * // => true + * + * _.isWeakSet(new Set); + * // => false + */ + function isWeakSet(value) { + return isObjectLike(value) && baseGetTag(value) == weakSetTag; + } + + /** + * Checks if `value` is less than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + * @see _.gt + * @example + * + * _.lt(1, 3); + * // => true + * + * _.lt(3, 3); + * // => false + * + * _.lt(3, 1); + * // => false + */ + var lt = createRelationalOperation(baseLt); + + /** + * Checks if `value` is less than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than or equal to + * `other`, else `false`. + * @see _.gte + * @example + * + * _.lte(1, 3); + * // => true + * + * _.lte(3, 3); + * // => true + * + * _.lte(3, 1); + * // => false + */ + var lte = createRelationalOperation(function(value, other) { + return value <= other; + }); + + /** + * Converts `value` to an array. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * _.toArray({ 'a': 1, 'b': 2 }); + * // => [1, 2] + * + * _.toArray('abc'); + * // => ['a', 'b', 'c'] + * + * _.toArray(1); + * // => [] + * + * _.toArray(null); + * // => [] + */ + function toArray(value) { + if (!value) { + return []; + } + if (isArrayLike(value)) { + return isString(value) ? stringToArray(value) : copyArray(value); + } + if (symIterator && value[symIterator]) { + return iteratorToArray(value[symIterator]()); + } + var tag = getTag(value), + func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values); + + return func(value); + } + + /** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = (value < 0 ? -1 : 1); + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + + /** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ + function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + + return result === result ? (remainder ? result - remainder : result) : 0; + } + + /** + * Converts `value` to an integer suitable for use as the length of an + * array-like object. + * + * **Note:** This method is based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toLength(3.2); + * // => 3 + * + * _.toLength(Number.MIN_VALUE); + * // => 0 + * + * _.toLength(Infinity); + * // => 4294967295 + * + * _.toLength('3.2'); + * // => 3 + */ + function toLength(value) { + return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; + } + + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = baseTrim(value); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); + } + + /** + * Converts `value` to a plain object flattening inherited enumerable string + * keyed properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return copyObject(value, keysIn(value)); + } + + /** + * Converts `value` to a safe integer. A safe integer can be compared and + * represented correctly. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toSafeInteger(3.2); + * // => 3 + * + * _.toSafeInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toSafeInteger(Infinity); + * // => 9007199254740991 + * + * _.toSafeInteger('3.2'); + * // => 3 + */ + function toSafeInteger(value) { + return value + ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) + : (value === 0 ? value : 0); + } + + /** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ + function toString(value) { + return value == null ? '' : baseToString(value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable string keyed properties of source objects to the + * destination object. Source objects are applied from left to right. + * Subsequent sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object` and is loosely based on + * [`Object.assign`](https://mdn.io/Object/assign). + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assignIn + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assign({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'c': 3 } + */ + var assign = createAssigner(function(object, source) { + if (isPrototype(source) || isArrayLike(source)) { + copyObject(source, keys(source), object); + return; + } + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + assignValue(object, key, source[key]); + } + } + }); + + /** + * This method is like `_.assign` except that it iterates over own and + * inherited source properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assign + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assignIn({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } + */ + var assignIn = createAssigner(function(object, source) { + copyObject(source, keysIn(source), object); + }); + + /** + * This method is like `_.assignIn` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extendWith + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignInWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignInWith = createAssigner(function(object, source, srcIndex, customizer) { + copyObject(source, keysIn(source), object, customizer); + }); + + /** + * This method is like `_.assign` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignInWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignWith = createAssigner(function(object, source, srcIndex, customizer) { + copyObject(source, keys(source), object, customizer); + }); + + /** + * Creates an array of values corresponding to `paths` of `object`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Array} Returns the picked values. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _.at(object, ['a[0].b.c', 'a[1]']); + * // => [3, 4] + */ + var at = flatRest(baseAt); + + /** + * Creates an object that inherits from the `prototype` object. If a + * `properties` object is given, its own enumerable string keyed properties + * are assigned to the created object. + * + * @static + * @memberOf _ + * @since 2.3.0 + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * function Circle() { + * Shape.call(this); + * } + * + * Circle.prototype = _.create(Shape.prototype, { + * 'constructor': Circle + * }); + * + * var circle = new Circle; + * circle instanceof Circle; + * // => true + * + * circle instanceof Shape; + * // => true + */ + function create(prototype, properties) { + var result = baseCreate(prototype); + return properties == null ? result : baseAssign(result, properties); + } + + /** + * Assigns own and inherited enumerable string keyed properties of source + * objects to the destination object for all destination properties that + * resolve to `undefined`. Source objects are applied from left to right. + * Once a property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaultsDeep + * @example + * + * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var defaults = baseRest(function(object, sources) { + object = Object(object); + + var index = -1; + var length = sources.length; + var guard = length > 2 ? sources[2] : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + length = 1; + } + + while (++index < length) { + var source = sources[index]; + var props = keysIn(source); + var propsIndex = -1; + var propsLength = props.length; + + while (++propsIndex < propsLength) { + var key = props[propsIndex]; + var value = object[key]; + + if (value === undefined || + (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) { + object[key] = source[key]; + } + } + } + + return object; + }); + + /** + * This method is like `_.defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaults + * @example + * + * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); + * // => { 'a': { 'b': 2, 'c': 3 } } + */ + var defaultsDeep = baseRest(function(args) { + args.push(undefined, customDefaultsMerge); + return apply(mergeWith, undefined, args); + }); + + /** + * This method is like `_.find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findKey(users, function(o) { return o.age < 40; }); + * // => 'barney' (iteration order is not guaranteed) + * + * // The `_.matches` iteratee shorthand. + * _.findKey(users, { 'age': 1, 'active': true }); + * // => 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findKey(users, 'active'); + * // => 'barney' + */ + function findKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); + } + + /** + * This method is like `_.findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findLastKey(users, function(o) { return o.age < 40; }); + * // => returns 'pebbles' assuming `_.findKey` returns 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.findLastKey(users, { 'age': 36, 'active': true }); + * // => 'barney' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findLastKey(users, 'active'); + * // => 'pebbles' + */ + function findLastKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); + } + + /** + * Iterates over own and inherited enumerable string keyed properties of an + * object and invokes `iteratee` for each property. The iteratee is invoked + * with three arguments: (value, key, object). Iteratee functions may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forInRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forIn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). + */ + function forIn(object, iteratee) { + return object == null + ? object + : baseFor(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * This method is like `_.forIn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forIn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forInRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. + */ + function forInRight(object, iteratee) { + return object == null + ? object + : baseForRight(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * Iterates over own enumerable string keyed properties of an object and + * invokes `iteratee` for each property. The iteratee is invoked with three + * arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwnRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forOwn(object, iteratee) { + return object && baseForOwn(object, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwnRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. + */ + function forOwnRight(object, iteratee) { + return object && baseForOwnRight(object, getIteratee(iteratee, 3)); + } + + /** + * Creates an array of function property names from own enumerable properties + * of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functionsIn + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functions(new Foo); + * // => ['a', 'b'] + */ + function functions(object) { + return object == null ? [] : baseFunctions(object, keys(object)); + } + + /** + * Creates an array of function property names from own and inherited + * enumerable properties of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functions + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functionsIn(new Foo); + * // => ['a', 'b', 'c'] + */ + function functionsIn(object) { + return object == null ? [] : baseFunctions(object, keysIn(object)); + } + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = { 'a': { 'b': 2 } }; + * var other = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b'); + * // => true + * + * _.has(object, ['a', 'b']); + * // => true + * + * _.has(other, 'a'); + * // => false + */ + function has(object, path) { + return object != null && hasPath(object, path, baseHas); + } + + /** + * Checks if `path` is a direct or inherited property of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.hasIn(object, 'a'); + * // => true + * + * _.hasIn(object, 'a.b'); + * // => true + * + * _.hasIn(object, ['a', 'b']); + * // => true + * + * _.hasIn(object, 'b'); + * // => false + */ + function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); + } + + /** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite + * property assignments of previous values. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Object + * @param {Object} object The object to invert. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invert(object); + * // => { '1': 'c', '2': 'b' } + */ + var invert = createInverter(function(result, value, key) { + if (value != null && + typeof value.toString != 'function') { + value = nativeObjectToString.call(value); + } + + result[value] = key; + }, constant(identity)); + + /** + * This method is like `_.invert` except that the inverted object is generated + * from the results of running each element of `object` thru `iteratee`. The + * corresponding inverted value of each inverted key is an array of keys + * responsible for generating the inverted value. The iteratee is invoked + * with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Object + * @param {Object} object The object to invert. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invertBy(object); + * // => { '1': ['a', 'c'], '2': ['b'] } + * + * _.invertBy(object, function(value) { + * return 'group' + value; + * }); + * // => { 'group1': ['a', 'c'], 'group2': ['b'] } + */ + var invertBy = createInverter(function(result, value, key) { + if (value != null && + typeof value.toString != 'function') { + value = nativeObjectToString.call(value); + } + + if (hasOwnProperty.call(result, value)) { + result[value].push(key); + } else { + result[value] = [key]; + } + }, getIteratee); + + /** + * Invokes the method at `path` of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + * @example + * + * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; + * + * _.invoke(object, 'a[0].b.c.slice', 1, 3); + * // => [2, 3] + */ + var invoke = baseRest(baseInvoke); + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + } + + /** + * The opposite of `_.mapValues`; this method creates an object with the + * same values as `object` and keys generated by running each own enumerable + * string keyed property of `object` thru `iteratee`. The iteratee is invoked + * with three arguments: (value, key, object). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapValues + * @example + * + * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { + * return key + value; + * }); + * // => { 'a1': 1, 'b2': 2 } + */ + function mapKeys(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + + baseForOwn(object, function(value, key, object) { + baseAssignValue(result, iteratee(value, key, object), value); + }); + return result; + } + + /** + * Creates an object with the same keys as `object` and values generated + * by running each own enumerable string keyed property of `object` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, key, object). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapKeys + * @example + * + * var users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * }; + * + * _.mapValues(users, function(o) { return o.age; }); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + * + * // The `_.property` iteratee shorthand. + * _.mapValues(users, 'age'); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */ + function mapValues(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + + baseForOwn(object, function(value, key, object) { + baseAssignValue(result, key, iteratee(value, key, object)); + }); + return result; + } + + /** + * This method is like `_.assign` except that it recursively merges own and + * inherited enumerable string keyed properties of source objects into the + * destination object. Source properties that resolve to `undefined` are + * skipped if a destination value exists. Array and plain object properties + * are merged recursively. Other objects and value types are overridden by + * assignment. Source objects are applied from left to right. Subsequent + * sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * var object = { + * 'a': [{ 'b': 2 }, { 'd': 4 }] + * }; + * + * var other = { + * 'a': [{ 'c': 3 }, { 'e': 5 }] + * }; + * + * _.merge(object, other); + * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } + */ + var merge = createAssigner(function(object, source, srcIndex) { + baseMerge(object, source, srcIndex); + }); + + /** + * This method is like `_.merge` except that it accepts `customizer` which + * is invoked to produce the merged values of the destination and source + * properties. If `customizer` returns `undefined`, merging is handled by the + * method instead. The `customizer` is invoked with six arguments: + * (objValue, srcValue, key, object, source, stack). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * function customizer(objValue, srcValue) { + * if (_.isArray(objValue)) { + * return objValue.concat(srcValue); + * } + * } + * + * var object = { 'a': [1], 'b': [2] }; + * var other = { 'a': [3], 'b': [4] }; + * + * _.mergeWith(object, other, customizer); + * // => { 'a': [1, 3], 'b': [2, 4] } + */ + var mergeWith = createAssigner(function(object, source, srcIndex, customizer) { + baseMerge(object, source, srcIndex, customizer); + }); + + /** + * The opposite of `_.pick`; this method creates an object composed of the + * own and inherited enumerable property paths of `object` that are not omitted. + * + * **Note:** This method is considerably slower than `_.pick`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to omit. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omit(object, ['a', 'c']); + * // => { 'b': '2' } + */ + var omit = flatRest(function(object, paths) { + var result = {}; + if (object == null) { + return result; + } + var isDeep = false; + paths = arrayMap(paths, function(path) { + path = castPath(path, object); + isDeep || (isDeep = path.length > 1); + return path; + }); + copyObject(object, getAllKeysIn(object), result); + if (isDeep) { + result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); + } + var length = paths.length; + while (length--) { + baseUnset(result, paths[length]); + } + return result; + }); + + /** + * The opposite of `_.pickBy`; this method creates an object composed of + * the own and inherited enumerable string keyed properties of `object` that + * `predicate` doesn't return truthy for. The predicate is invoked with two + * arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omitBy(object, _.isNumber); + * // => { 'b': '2' } + */ + function omitBy(object, predicate) { + return pickBy(object, negate(getIteratee(predicate))); + } + + /** + * Creates an object composed of the picked `object` properties. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pick(object, ['a', 'c']); + * // => { 'a': 1, 'c': 3 } + */ + var pick = flatRest(function(object, paths) { + return object == null ? {} : basePick(object, paths); + }); + + /** + * Creates an object composed of the `object` properties `predicate` returns + * truthy for. The predicate is invoked with two arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pickBy(object, _.isNumber); + * // => { 'a': 1, 'c': 3 } + */ + function pickBy(object, predicate) { + if (object == null) { + return {}; + } + var props = arrayMap(getAllKeysIn(object), function(prop) { + return [prop]; + }); + predicate = getIteratee(predicate); + return basePickBy(object, props, function(value, path) { + return predicate(value, path[0]); + }); + } + + /** + * This method is like `_.get` except that if the resolved value is a + * function it's invoked with the `this` binding of its parent object and + * its result is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a[0].b.c3', 'default'); + * // => 'default' + * + * _.result(object, 'a[0].b.c3', _.constant('default')); + * // => 'default' + */ + function result(object, path, defaultValue) { + path = castPath(path, object); + + var index = -1, + length = path.length; + + // Ensure the loop is entered when path is empty. + if (!length) { + length = 1; + object = undefined; + } + while (++index < length) { + var value = object == null ? undefined : object[toKey(path[index])]; + if (value === undefined) { + index = length; + value = defaultValue; + } + object = isFunction(value) ? value.call(object) : value; + } + return object; + } + + /** + * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, + * it's created. Arrays are created for missing index properties while objects + * are created for all other missing properties. Use `_.setWith` to customize + * `path` creation. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, ['x', '0', 'y', 'z'], 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + return object == null ? object : baseSet(object, path, value); + } + + /** + * This method is like `_.set` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.setWith(object, '[0][1]', 'a', Object); + * // => { '0': { '1': 'a' } } + */ + function setWith(object, path, value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseSet(object, path, value, customizer); + } + + /** + * Creates an array of own enumerable string keyed-value pairs for `object` + * which can be consumed by `_.fromPairs`. If `object` is a map or set, its + * entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entries + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairs(new Foo); + * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) + */ + var toPairs = createToPairs(keys); + + /** + * Creates an array of own and inherited enumerable string keyed-value pairs + * for `object` which can be consumed by `_.fromPairs`. If `object` is a map + * or set, its entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entriesIn + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairsIn(new Foo); + * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) + */ + var toPairsIn = createToPairs(keysIn); + + /** + * An alternative to `_.reduce`; this method transforms `object` to a new + * `accumulator` object which is the result of running each of its own + * enumerable string keyed properties thru `iteratee`, with each invocation + * potentially mutating the `accumulator` object. If `accumulator` is not + * provided, a new object with the same `[[Prototype]]` will be used. The + * iteratee is invoked with four arguments: (accumulator, value, key, object). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The custom accumulator value. + * @returns {*} Returns the accumulated value. + * @example + * + * _.transform([2, 3, 4], function(result, n) { + * result.push(n *= n); + * return n % 2 == 0; + * }, []); + * // => [4, 9] + * + * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } + */ + function transform(object, iteratee, accumulator) { + var isArr = isArray(object), + isArrLike = isArr || isBuffer(object) || isTypedArray(object); + + iteratee = getIteratee(iteratee, 4); + if (accumulator == null) { + var Ctor = object && object.constructor; + if (isArrLike) { + accumulator = isArr ? new Ctor : []; + } + else if (isObject(object)) { + accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; + } + else { + accumulator = {}; + } + } + (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) { + return iteratee(accumulator, value, index, object); + }); + return accumulator; + } + + /** + * Removes the property at `path` of `object`. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 7 } }] }; + * _.unset(object, 'a[0].b.c'); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + * + * _.unset(object, ['a', '0', 'b', 'c']); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + */ + function unset(object, path) { + return object == null ? true : baseUnset(object, path); + } + + /** + * This method is like `_.set` except that accepts `updater` to produce the + * value to set. Use `_.updateWith` to customize `path` creation. The `updater` + * is invoked with one argument: (value). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.update(object, 'a[0].b.c', function(n) { return n * n; }); + * console.log(object.a[0].b.c); + * // => 9 + * + * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); + * console.log(object.x[0].y.z); + * // => 0 + */ + function update(object, path, updater) { + return object == null ? object : baseUpdate(object, path, castFunction(updater)); + } + + /** + * This method is like `_.update` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.updateWith(object, '[0][1]', _.constant('a'), Object); + * // => { '0': { '1': 'a' } } + */ + function updateWith(object, path, updater, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); + } + + /** + * Creates an array of the own enumerable string keyed property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */ + function values(object) { + return object == null ? [] : baseValues(object, keys(object)); + } + + /** + * Creates an array of the own and inherited enumerable string keyed property + * values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.valuesIn(new Foo); + * // => [1, 2, 3] (iteration order is not guaranteed) + */ + function valuesIn(object) { + return object == null ? [] : baseValues(object, keysIn(object)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Clamps `number` within the inclusive `lower` and `upper` bounds. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Number + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + * @example + * + * _.clamp(-10, -5, 5); + * // => -5 + * + * _.clamp(10, -5, 5); + * // => 5 + */ + function clamp(number, lower, upper) { + if (upper === undefined) { + upper = lower; + lower = undefined; + } + if (upper !== undefined) { + upper = toNumber(upper); + upper = upper === upper ? upper : 0; + } + if (lower !== undefined) { + lower = toNumber(lower); + lower = lower === lower ? lower : 0; + } + return baseClamp(toNumber(number), lower, upper); + } + + /** + * Checks if `n` is between `start` and up to, but not including, `end`. If + * `end` is not specified, it's set to `start` with `start` then set to `0`. + * If `start` is greater than `end` the params are swapped to support + * negative ranges. + * + * @static + * @memberOf _ + * @since 3.3.0 + * @category Number + * @param {number} number The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + * @see _.range, _.rangeRight + * @example + * + * _.inRange(3, 2, 4); + * // => true + * + * _.inRange(4, 8); + * // => true + * + * _.inRange(4, 2); + * // => false + * + * _.inRange(2, 2); + * // => false + * + * _.inRange(1.2, 2); + * // => true + * + * _.inRange(5.2, 4); + * // => false + * + * _.inRange(-3, -2, -6); + * // => true + */ + function inRange(number, start, end) { + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + number = toNumber(number); + return baseInRange(number, start, end); + } + + /** + * Produces a random number between the inclusive `lower` and `upper` bounds. + * If only one argument is provided a number between `0` and the given number + * is returned. If `floating` is `true`, or either `lower` or `upper` are + * floats, a floating-point number is returned instead of an integer. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Number + * @param {number} [lower=0] The lower bound. + * @param {number} [upper=1] The upper bound. + * @param {boolean} [floating] Specify returning a floating-point number. + * @returns {number} Returns the random number. + * @example + * + * _.random(0, 5); + * // => an integer between 0 and 5 + * + * _.random(5); + * // => also an integer between 0 and 5 + * + * _.random(5, true); + * // => a floating-point number between 0 and 5 + * + * _.random(1.2, 5.2); + * // => a floating-point number between 1.2 and 5.2 + */ + function random(lower, upper, floating) { + if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { + upper = floating = undefined; + } + if (floating === undefined) { + if (typeof upper == 'boolean') { + floating = upper; + upper = undefined; + } + else if (typeof lower == 'boolean') { + floating = lower; + lower = undefined; + } + } + if (lower === undefined && upper === undefined) { + lower = 0; + upper = 1; + } + else { + lower = toFinite(lower); + if (upper === undefined) { + upper = lower; + lower = 0; + } else { + upper = toFinite(upper); + } + } + if (lower > upper) { + var temp = lower; + lower = upper; + upper = temp; + } + if (floating || lower % 1 || upper % 1) { + var rand = nativeRandom(); + return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper); + } + return baseRandom(lower, upper); + } + + /*------------------------------------------------------------------------*/ + + /** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @example + * + * _.camelCase('Foo Bar'); + * // => 'fooBar' + * + * _.camelCase('--foo-bar--'); + * // => 'fooBar' + * + * _.camelCase('__FOO_BAR__'); + * // => 'fooBar' + */ + var camelCase = createCompounder(function(result, word, index) { + word = word.toLowerCase(); + return result + (index ? capitalize(word) : word); + }); + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * _.capitalize('FRED'); + * // => 'Fred' + */ + function capitalize(string) { + return upperFirst(toString(string).toLowerCase()); + } + + /** + * Deburrs `string` by converting + * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) + * letters to basic Latin letters and removing + * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */ + function deburr(string) { + string = toString(string); + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); + } + + /** + * Checks if `string` ends with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search up to. + * @returns {boolean} Returns `true` if `string` ends with `target`, + * else `false`. + * @example + * + * _.endsWith('abc', 'c'); + * // => true + * + * _.endsWith('abc', 'b'); + * // => false + * + * _.endsWith('abc', 'b', 2); + * // => true + */ + function endsWith(string, target, position) { + string = toString(string); + target = baseToString(target); + + var length = string.length; + position = position === undefined + ? length + : baseClamp(toInteger(position), 0, length); + + var end = position; + position -= target.length; + return position >= 0 && string.slice(position, end) == target; + } + + /** + * Converts the characters "&", "<", ">", '"', and "'" in `string` to their + * corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional + * characters use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. See + * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * When working with HTML you should always + * [quote attribute values](http://wonko.com/post/html-escaping) to reduce + * XSS vectors. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function escape(string) { + string = toString(string); + return (string && reHasUnescapedHtml.test(string)) + ? string.replace(reUnescapedHtml, escapeHtmlChar) + : string; + } + + /** + * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", + * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escapeRegExp('[lodash](https://lodash.com/)'); + * // => '\[lodash\]\(https://lodash\.com/\)' + */ + function escapeRegExp(string) { + string = toString(string); + return (string && reHasRegExpChar.test(string)) + ? string.replace(reRegExpChar, '\\$&') + : string; + } + + /** + * Converts `string` to + * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__FOO_BAR__'); + * // => 'foo-bar' + */ + var kebabCase = createCompounder(function(result, word, index) { + return result + (index ? '-' : '') + word.toLowerCase(); + }); + + /** + * Converts `string`, as space separated words, to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the lower cased string. + * @example + * + * _.lowerCase('--Foo-Bar--'); + * // => 'foo bar' + * + * _.lowerCase('fooBar'); + * // => 'foo bar' + * + * _.lowerCase('__FOO_BAR__'); + * // => 'foo bar' + */ + var lowerCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + word.toLowerCase(); + }); + + /** + * Converts the first character of `string` to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.lowerFirst('Fred'); + * // => 'fred' + * + * _.lowerFirst('FRED'); + * // => 'fRED' + */ + var lowerFirst = createCaseFirst('toLowerCase'); + + /** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.pad('abc', 8); + * // => ' abc ' + * + * _.pad('abc', 8, '_-'); + * // => '_-abc_-_' + * + * _.pad('abc', 3); + * // => 'abc' + */ + function pad(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + if (!length || strLength >= length) { + return string; + } + var mid = (length - strLength) / 2; + return ( + createPadding(nativeFloor(mid), chars) + + string + + createPadding(nativeCeil(mid), chars) + ); + } + + /** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padEnd('abc', 6); + * // => 'abc ' + * + * _.padEnd('abc', 6, '_-'); + * // => 'abc_-_' + * + * _.padEnd('abc', 3); + * // => 'abc' + */ + function padEnd(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + return (length && strLength < length) + ? (string + createPadding(length - strLength, chars)) + : string; + } + + /** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padStart('abc', 6); + * // => ' abc' + * + * _.padStart('abc', 6, '_-'); + * // => '_-_abc' + * + * _.padStart('abc', 3); + * // => 'abc' + */ + function padStart(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + return (length && strLength < length) + ? (createPadding(length - strLength, chars) + string) + : string; + } + + /** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `value` is a + * hexadecimal, in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the + * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category String + * @param {string} string The string to convert. + * @param {number} [radix=10] The radix to interpret `value` by. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {number} Returns the converted integer. + * @example + * + * _.parseInt('08'); + * // => 8 + * + * _.map(['6', '08', '10'], _.parseInt); + * // => [6, 8, 10] + */ + function parseInt(string, radix, guard) { + if (guard || radix == null) { + radix = 0; + } else if (radix) { + radix = +radix; + } + return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); + } + + /** + * Repeats the given string `n` times. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=1] The number of times to repeat the string. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {string} Returns the repeated string. + * @example + * + * _.repeat('*', 3); + * // => '***' + * + * _.repeat('abc', 2); + * // => 'abcabc' + * + * _.repeat('abc', 0); + * // => '' + */ + function repeat(string, n, guard) { + if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) { + n = 1; + } else { + n = toInteger(n); + } + return baseRepeat(toString(string), n); + } + + /** + * Replaces matches for `pattern` in `string` with `replacement`. + * + * **Note:** This method is based on + * [`String#replace`](https://mdn.io/String/replace). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to modify. + * @param {RegExp|string} pattern The pattern to replace. + * @param {Function|string} replacement The match replacement. + * @returns {string} Returns the modified string. + * @example + * + * _.replace('Hi Fred', 'Fred', 'Barney'); + * // => 'Hi Barney' + */ + function replace() { + var args = arguments, + string = toString(args[0]); + + return args.length < 3 ? string : string.replace(args[1], args[2]); + } + + /** + * Converts `string` to + * [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @example + * + * _.snakeCase('Foo Bar'); + * // => 'foo_bar' + * + * _.snakeCase('fooBar'); + * // => 'foo_bar' + * + * _.snakeCase('--FOO-BAR--'); + * // => 'foo_bar' + */ + var snakeCase = createCompounder(function(result, word, index) { + return result + (index ? '_' : '') + word.toLowerCase(); + }); + + /** + * Splits `string` by `separator`. + * + * **Note:** This method is based on + * [`String#split`](https://mdn.io/String/split). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to split. + * @param {RegExp|string} separator The separator pattern to split by. + * @param {number} [limit] The length to truncate results to. + * @returns {Array} Returns the string segments. + * @example + * + * _.split('a-b-c', '-', 2); + * // => ['a', 'b'] + */ + function split(string, separator, limit) { + if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { + separator = limit = undefined; + } + limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; + if (!limit) { + return []; + } + string = toString(string); + if (string && ( + typeof separator == 'string' || + (separator != null && !isRegExp(separator)) + )) { + separator = baseToString(separator); + if (!separator && hasUnicode(string)) { + return castSlice(stringToArray(string), 0, limit); + } + } + return string.split(separator, limit); + } + + /** + * Converts `string` to + * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @static + * @memberOf _ + * @since 3.1.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @example + * + * _.startCase('--foo-bar--'); + * // => 'Foo Bar' + * + * _.startCase('fooBar'); + * // => 'Foo Bar' + * + * _.startCase('__FOO_BAR__'); + * // => 'FOO BAR' + */ + var startCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + upperFirst(word); + }); + + /** + * Checks if `string` starts with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, + * else `false`. + * @example + * + * _.startsWith('abc', 'a'); + * // => true + * + * _.startsWith('abc', 'b'); + * // => false + * + * _.startsWith('abc', 'b', 1); + * // => true + */ + function startsWith(string, target, position) { + string = toString(string); + position = position == null + ? 0 + : baseClamp(toInteger(position), 0, string.length); + + target = baseToString(target); + return string.slice(position, position + target.length) == target; + } + + /** + * Creates a compiled template function that can interpolate data properties + * in "interpolate" delimiters, HTML-escape interpolated data properties in + * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data + * properties may be accessed as free variables in the template. If a setting + * object is given, it takes precedence over `_.templateSettings` values. + * + * **Note:** In the development build `_.template` utilizes + * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * for easier debugging. + * + * For more information on precompiling templates see + * [lodash's custom builds documentation](https://lodash.com/custom-builds). + * + * For more information on Chrome extension sandboxes see + * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The template string. + * @param {Object} [options={}] The options object. + * @param {RegExp} [options.escape=_.templateSettings.escape] + * The HTML "escape" delimiter. + * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] + * The "evaluate" delimiter. + * @param {Object} [options.imports=_.templateSettings.imports] + * An object to import into the template as free variables. + * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] + * The "interpolate" delimiter. + * @param {string} [options.sourceURL='lodash.templateSources[n]'] + * The sourceURL of the compiled template. + * @param {string} [options.variable='obj'] + * The data object variable name. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the compiled template function. + * @example + * + * // Use the "interpolate" delimiter to create a compiled template. + * var compiled = _.template('hello <%= user %>!'); + * compiled({ 'user': 'fred' }); + * // => 'hello fred!' + * + * // Use the HTML "escape" delimiter to escape data property values. + * var compiled = _.template('<%- value %>'); + * compiled({ 'value': '