UNPKG

@tensorflow/tfjs-core

Version:

Hardware-accelerated JavaScript library for machine intelligence

175 lines 23 kB
import * as util from '../util'; /** * Wraps a list of ArrayBuffers into a `slice()`-able object without allocating * a large ArrayBuffer. * * Allocating large ArrayBuffers (~2GB) can be unstable on Chrome. TFJS loads * its weights as a list of (usually) 4MB ArrayBuffers and then slices the * weight tensors out of them. For small models, it's safe to concatenate all * the weight buffers into a single ArrayBuffer and then slice the weight * tensors out of it, but for large models, a different approach is needed. */ export class CompositeArrayBuffer { /** * Concatenate a number of ArrayBuffers into one. * * @param buffers An array of ArrayBuffers to concatenate, or a single * ArrayBuffer. * @returns Result of concatenating `buffers` in order. */ static join(buffers) { return new CompositeArrayBuffer(buffers).slice(); } constructor(buffers) { this.shards = []; this.previousShardIndex = 0; if (buffers == null) { return; } // Normalize the `buffers` input to be `ArrayBuffer[]`. if (!(buffers instanceof Array)) { buffers = [buffers]; } buffers = buffers.map((bufferOrTypedArray) => { if (util.isTypedArray(bufferOrTypedArray)) { return bufferOrTypedArray.buffer; } return bufferOrTypedArray; }); // Skip setting up shards if there are no buffers. if (buffers.length === 0) { return; } this.bufferUniformSize = buffers[0].byteLength; let start = 0; for (let i = 0; i < buffers.length; i++) { const buffer = buffers[i]; // Check that all buffers except the last one have the same length. if (i !== buffers.length - 1 && buffer.byteLength !== this.bufferUniformSize) { // Unset the buffer uniform size, since the buffer sizes are not // uniform. this.bufferUniformSize = undefined; } // Create the shards, including their start and end points. const end = start + buffer.byteLength; this.shards.push({ buffer, start, end }); start = end; } // Set the byteLength if (this.shards.length === 0) { this.byteLength = 0; } this.byteLength = this.shards[this.shards.length - 1].end; } slice(start = 0, end = this.byteLength) { // If there are no shards, then the CompositeArrayBuffer was initialized // with no data. if (this.shards.length === 0) { return new ArrayBuffer(0); } // NaN is treated as zero for slicing. This matches ArrayBuffer's behavior. start = isNaN(Number(start)) ? 0 : start; end = isNaN(Number(end)) ? 0 : end; // Fix the bounds to within the array. start = Math.max(0, start); end = Math.min(this.byteLength, end); if (end <= start) { return new ArrayBuffer(0); } const startShardIndex = this.findShardForByte(start); if (startShardIndex === -1) { // This should not happen since the start and end indices are always // within 0 and the composite array's length. throw new Error(`Could not find start shard for byte ${start}`); } const size = end - start; const outputBuffer = new ArrayBuffer(size); const outputArray = new Uint8Array(outputBuffer); let sliced = 0; for (let i = startShardIndex; i < this.shards.length; i++) { const shard = this.shards[i]; const globalStart = start + sliced; const localStart = globalStart - shard.start; const outputStart = sliced; const globalEnd = Math.min(end, shard.end); const localEnd = globalEnd - shard.start; const outputSlice = new Uint8Array(shard.buffer, localStart, localEnd - localStart); outputArray.set(outputSlice, outputStart); sliced += outputSlice.length; if (end < shard.end) { break; } } return outputBuffer; } /** * Get the index of the shard that contains the byte at `byteIndex`. */ findShardForByte(byteIndex) { if (this.shards.length === 0 || byteIndex < 0 || byteIndex >= this.byteLength) { return -1; } // If the buffers have a uniform size, compute the shard directly. if (this.bufferUniformSize != null) { this.previousShardIndex = Math.floor(byteIndex / this.bufferUniformSize); return this.previousShardIndex; } // If the buffers don't have a uniform size, we need to search for the // shard. That means we need a function to check where the byteIndex lies // relative to a given shard. function check(shard) { if (byteIndex < shard.start) { return -1; } if (byteIndex >= shard.end) { return 1; } return 0; } // For efficiency, try the previous shard first. if (check(this.shards[this.previousShardIndex]) === 0) { return this.previousShardIndex; } // Otherwise, use a generic search function. // This should almost never end up being used in practice since the weight // entries should always be in order. const index = search(this.shards, check); if (index === -1) { return -1; } this.previousShardIndex = index; return this.previousShardIndex; } } /** * Search for an element of a sorted array. * * @param sortedArray The sorted array to search * @param compare A function to compare the current value against the searched * value. Return 0 on a match, negative if the searched value is less than * the value passed to the function, and positive if the searched value is * greater than the value passed to the function. * @returns The index of the element, or -1 if it's not in the array. */ export function search(sortedArray, compare) { // Binary search let min = 0; let max = sortedArray.length; while (min <= max) { const middle = Math.floor((max - min) / 2) + min; const side = compare(sortedArray[middle]); if (side === 0) { return middle; } else if (side < 0) { max = middle; } else { min = middle + 1; } } return -1; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9zaXRlX2FycmF5X2J1ZmZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3RmanMtY29yZS9zcmMvaW8vY29tcG9zaXRlX2FycmF5X2J1ZmZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFpQkEsT0FBTyxLQUFLLElBQUksTUFBTSxTQUFTLENBQUM7QUFRaEM7Ozs7Ozs7OztHQVNHO0FBRUgsTUFBTSxPQUFPLG9CQUFvQjtJQU0vQjs7Ozs7O09BTUc7SUFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQXFDO1FBQy9DLE9BQU8sSUFBSSxvQkFBb0IsQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNuRCxDQUFDO0lBRUQsWUFBWSxPQUNFO1FBakJOLFdBQU0sR0FBa0IsRUFBRSxDQUFDO1FBQzNCLHVCQUFrQixHQUFHLENBQUMsQ0FBQztRQWlCN0IsSUFBSSxPQUFPLElBQUksSUFBSSxFQUFFO1lBQ25CLE9BQU87U0FDUjtRQUNELHVEQUF1RDtRQUN2RCxJQUFJLENBQUMsQ0FBQyxPQUFPLFlBQVksS0FBSyxDQUFDLEVBQUU7WUFDL0IsT0FBTyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7U0FDckI7UUFDRCxPQUFPLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLGtCQUFrQixFQUFFLEVBQUU7WUFDM0MsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLGtCQUFrQixDQUFDLEVBQUU7Z0JBQ3pDLE9BQU8sa0JBQWtCLENBQUMsTUFBTSxDQUFDO2FBQ2xDO1lBQ0QsT0FBTyxrQkFBa0IsQ0FBQztRQUM1QixDQUFDLENBQUMsQ0FBQztRQUVILGtEQUFrRDtRQUNsRCxJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1lBQ3hCLE9BQU87U0FDUjtRQUVELElBQUksQ0FBQyxpQkFBaUIsR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDO1FBQy9DLElB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QU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUUxQyxJQUFJLElBQUksS0FBSyxDQUFDLEVBQUU7WUFDZCxPQUFPLE1BQU0sQ0FBQztTQUNmO2FBQU0sSUFBSSxJQUFJLEdBQUcsQ0FBQyxFQUFFO1lBQ25CLEdBQUcsR0FBRyxNQUFNLENBQUM7U0FDZDthQUFNO1lBQ0wsR0FBRyxHQUFHLE1BQU0sR0FBRyxDQUFDLENBQUM7U0FDbEI7S0FDRjtJQUNELE9BQU8sQ0FBQyxDQUFDLENBQUM7QUFDWixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IDIwMjMgR29vZ2xlIExMQy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAqL1xuaW1wb3J0IHtUeXBlZEFycmF5fSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG50eXBlIEJ1ZmZlclNoYXJkID0ge1xuICBzdGFydDogbnVtYmVyLFxuICBlbmQ6IG51bWJlcixcbiAgYnVmZmVyOiBBcnJheUJ1ZmZlcixcbn07XG5cbi8qKlxuICogV3JhcHMgYSBsaXN0IG9mIEFycmF5QnVmZmVycyBpbnRvIGEgYHNsaWNlKClgLWFibGUgb2JqZWN0IHdpdGhvdXQgYWxsb2NhdGluZ1xuICogYSBsYXJnZSBBcnJheUJ1ZmZlci5cbiAqXG4gKiBBbGxvY2F0aW5nIGxhcmdlIEFycmF5QnVmZmVycyAofjJHQikgY2FuIGJlIHVuc3RhYmxlIG9uIENocm9tZS4gVEZKUyBsb2Fkc1xuICogaXRzIHdlaWdodHMgYXMgYSBsaXN0IG9mICh1c3VhbGx5KSA0TUIgQXJyYXlCdWZmZXJzIGFuZCB0aGVuIHNsaWNlcyB0aGVcbiAqIHdlaWdodCB0ZW5zb3JzIG91dCBvZiB0aGVtLiBGb3Igc21hbGwgbW9kZWxzLCBpdCdzIHNhZmUgdG8gY29uY2F0ZW5hdGUgYWxsXG4gKiB0aGUgd2VpZ2h0IGJ1ZmZlcnMgaW50byBhIHNpbmdsZSBBcnJheUJ1ZmZlciBhbmQgdGhlbiBzbGljZSB0aGUgd2VpZ2h0XG4gKiB0ZW5zb3JzIG91dCBvZiBpdCwgYnV0IGZvciBsYXJnZSBtb2RlbHMsIGEgZGlmZmVyZW50IGFwcHJvYWNoIGlzIG5lZWRlZC5cbiAqL1xuXG5leHBvcnQgY2xhc3MgQ29tcG9zaXRlQXJyYXlCdWZmZXIge1xuICBwcml2YXRlIHNoYXJkczogQnVmZmVyU2hhcmRbXSA9IFtdO1xuICBwcml2YXRlIHByZXZpb3VzU2hhcmRJbmRleCA9IDA7XG4gIHByaXZhdGUgYnVmZmVyVW5pZm9ybVNpemU/OiBudW1iZXI7XG4gIHB1YmxpYyByZWFkb25seSBieXRlTGVuZ3RoOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIENvbmNhdGVuYXRlIGEgbnVtYmVyIG9mIEFycmF5QnVmZmVycyBpbnRvIG9uZS5cbiAgICpcbiAgICogQHBhcmFtIGJ1ZmZlcnMgQW4gYXJyYXkgb2YgQXJyYXlCdWZmZXJzIHRvIGNvbmNhdGVuYXRlLCBvciBhIHNpbmdsZVxuICAgKiAgICAgQXJyYXlCdWZmZXIuXG4gICAqIEByZXR1cm5zIFJlc3VsdCBvZiBjb25jYXRlbmF0aW5nIGBidWZmZXJzYCBpbiBvcmRlci5cbiAgICovXG4gIHN0YXRpYyBqb2luKGJ1ZmZlcnM/OiBBcnJheUJ1ZmZlcltdIHwgQXJyYXlCdWZmZXIpIHtcbiAgICByZXR1cm4gbmV3IENvbXBvc2l0ZUFycmF5QnVmZmVyKGJ1ZmZlcnMpLnNsaWNlKCk7XG4gIH1cblxuICBjb25zdHJ1Y3RvcihidWZmZXJzPzogQXJyYXlCdWZmZXIgfCBBcnJheUJ1ZmZlcltdIHwgVHlwZWRBcnJheSB8XG4gICAgVHlwZWRBcnJheVtdKSB7XG4gICAgaWYgKGJ1ZmZlcnMgPT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBOb3JtYWxpemUgdGhlIGBidWZmZXJzYCBpbnB1dCB0byBiZSBgQXJyYXlCdWZmZXJbXWAuXG4gICAgaWYgKCEoYnVmZmVycyBpbnN0YW5jZW9mIEFycmF5KSkge1xuICAgICAgYnVmZmVycyA9IFtidWZmZXJzXTtcbiAgICB9XG4gICAgYnVmZmVycyA9IGJ1ZmZlcnMubWFwKChidWZmZXJPclR5cGVkQXJyYXkpID0+IHtcbiAgICAgIGlmICh1dGlsLmlzVHlwZWRBcnJheShidWZmZXJPclR5cGVkQXJyYXkpKSB7XG4gICAgICAgIHJldHVybiBidWZmZXJPclR5cGVkQXJyYXkuYnVmZmVyO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGJ1ZmZlck9yVHlwZWRBcnJheTtcbiAgICB9KTtcblxuICAgIC8vIFNraXAgc2V0dGluZyB1cCBzaGFyZHMgaWYgdGhlcmUgYXJlIG5vIGJ1ZmZlcnMuXG4gICAgaWYgKGJ1ZmZlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5idWZmZXJVbmlmb3JtU2l6ZSA9IGJ1ZmZlcnNbMF0uYnl0ZUxlbmd0aDtcbiAgICBsZXQgc3RhcnQgPSAwO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBidWZmZXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBidWZmZXIgPSBidWZmZXJzW2ldO1xuICAgICAgLy8gQ2hlY2sgdGhhdCBhbGwgYnVmZmVycyBleGNlcHQgdGhlIGxhc3Qgb25lIGhhdmUgdGhlIHNhbWUgbGVuZ3RoLlxuICAgICAgaWYgKGkgIT09IGJ1ZmZlcnMubGVuZ3RoIC0gMSAmJlxuICAgICAgICBidWZmZXIuYnl0ZUxlbmd0aCAhPT0gdGhpcy5idWZmZXJVbmlmb3JtU2l6ZSkge1xuICAgICAgICAvLyBVbnNldCB0aGUgYnVmZmVyIHVuaWZvcm0gc2l6ZSwgc2luY2UgdGhlIGJ1ZmZlciBzaXplcyBhcmUgbm90XG4gICAgICAgIC8vIHVuaWZvcm0uXG4gICAgICAgIHRoaXMuYnVmZmVyVW5pZm9ybVNpemUgPSB1bmRlZmluZWQ7XG4gICAgICB9XG5cbiAgICAgIC8vIENyZWF0ZSB0aGUgc2hhcmRzLCBpbmNsdWRpbmcgdGhlaXIgc3RhcnQgYW5kIGVuZCBwb2ludHMuXG4gICAgICBjb25zdCBlbmQgPSBzdGFydCArIGJ1ZmZlci5ieXRlTGVuZ3RoO1xuICAgICAgdGhpcy5zaGFyZHMucHVzaCh7IGJ1ZmZlciwgc3RhcnQsIGVuZCB9KTtcbiAgICAgIHN0YXJ0ID0gZW5kO1xuICAgIH1cblxuICAgIC8vIFNldCB0aGUgYnl0ZUxlbmd0aFxuICAgIGlmICh0aGlzLnNoYXJkcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRoaXMuYnl0ZUxlbmd0aCA9IDA7XG4gICAgfVxuICAgIHRoaXMuYnl0ZUxlbmd0aCA9IHRoaXMuc2hhcmRzW3RoaXMuc2hhcmRzLmxlbmd0aCAtIDFdLmVuZDtcbiAgfVxuXG4gIHNsaWNlKHN0YXJ0ID0gMCwgZW5kID0gdGhpcy5ieXRlTGVuZ3RoKTogQXJyYXlCdWZmZXIge1xuICAgIC8vIElmIHRoZXJlIGFyZSBubyBzaGFyZHMsIHRoZW4gdGhlIENvbXBvc2l0ZUFycmF5QnVmZmVyIHdhcyBpbml0aWFsaXplZFxuICAgIC8vIHdpdGggbm8gZGF0YS5cbiAgICBpZiAodGhpcy5zaGFyZHMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gbmV3IEFycmF5QnVmZmVyKDApO1xuICAgIH1cblxuICAgIC8vIE5hTiBpcyB0cmVhdGVkIGFzIHplcm8gZm9yIHNsaWNpbmcuIFRoaXMgbWF0Y2hlcyBBcnJheUJ1ZmZlcidzIGJlaGF2aW9yLlxuICAgIHN0YXJ0ID0gaXNOYU4oTnVtYmVyKHN0YXJ0KSkgPyAwIDogc3RhcnQ7XG4gICAgZW5kID0gaXNOYU4oTnVtYmVyKGVuZCkpID8gMCA6IGVuZDtcblxuICAgIC8vIEZpeCB0aGUgYm91bmRzIHRvIHdpdGhpbiB0aGUgYXJyYXkuXG4gICAgc3RhcnQgPSBNYXRoLm1heCgwLCBzdGFydCk7XG4gICAgZW5kID0gTWF0aC5taW4odGhpcy5ieXRlTGVuZ3RoLCBlbmQpO1xuICAgIGlmIChlbmQgPD0gc3RhcnQpIHtcbiAgICAgIHJldHVybiBuZXcgQXJyYXlCdWZmZXIoMCk7XG4gICAgfVxuXG4gICAgY29uc3Qgc3RhcnRTaGFyZEluZGV4ID0gdGhpcy5maW5kU2hhcmRGb3JCeXRlKHN0YXJ0KTtcbiAgICBpZiAoc3RhcnRTaGFyZEluZGV4ID09PSAtMSkge1xuICAgICAgLy8gVGhpcyBzaG91bGQgbm90IGhhcHBlbiBzaW5jZSB0aGUgc3RhcnQgYW5kIGVuZCBpbmRpY2VzIGFyZSBhbHdheXNcbiAgICAgIC8vIHdpdGhpbiAwIGFuZCB0aGUgY29tcG9zaXRlIGFycmF5J3MgbGVuZ3RoLlxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3QgZmluZCBzdGFydCBzaGFyZCBmb3IgYnl0ZSAke3N0YXJ0fWApO1xuICAgIH1cblxuICAgIGNvbnN0IHNpemUgPSBlbmQgLSBzdGFydDtcbiAgICBjb25zdCBvdXRwdXRCdWZmZXIgPSBuZXcgQXJyYXlCdWZmZXIoc2l6ZSk7XG4gICAgY29uc3Qgb3V0cHV0QXJyYXkgPSBuZXcgVWludDhBcnJheShvdXRwdXRCdWZmZXIpO1xuICAgIGxldCBzbGljZWQgPSAwO1xuICAgIGZvciAobGV0IGkgPSBzdGFydFNoYXJkSW5kZXg7IGkgPCB0aGlzLnNoYXJkcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3Qgc2hhcmQgPSB0aGlzLnNoYXJkc1tpXTtcblxuICAgICAgY29uc3QgZ2xvYmFsU3RhcnQgPSBzdGFydCArIHNsaWNlZDtcbiAgICAgIGNvbnN0IGxvY2FsU3RhcnQgPSBnbG9iYWxTdGFydCAtIHNoYXJkLnN0YXJ0O1xuICAgICAgY29uc3Qgb3V0cHV0U3RhcnQgPSBzbGljZWQ7XG5cbiAgICAgIGNvbnN0IGdsb2JhbEVuZCA9IE1hdGgubWluKGVuZCwgc2hhcmQuZW5kKTtcbiAgICAgIGNvbnN0IGxvY2FsRW5kID0gZ2xvYmFsRW5kIC0gc2hhcmQuc3RhcnQ7XG5cbiAgICAgIGNvbnN0IG91dHB1dFNsaWNlID0gbmV3IFVpbnQ4QXJyYXkoc2hhcmQuYnVmZmVyLCBsb2NhbFN0YXJ0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhbEVuZCAtIGxvY2FsU3RhcnQpO1xuICAgICAgb3V0cHV0QXJyYXkuc2V0KG91dHB1dFNsaWNlLCBvdXRwdXRTdGFydCk7XG4gICAgICBzbGljZWQgKz0gb3V0cHV0U2xpY2UubGVuZ3RoO1xuXG4gICAgICBpZiAoZW5kIDwgc2hhcmQuZW5kKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gb3V0cHV0QnVmZmVyO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgaW5kZXggb2YgdGhlIHNoYXJkIHRoYXQgY29udGFpbnMgdGhlIGJ5dGUgYXQgYGJ5dGVJbmRleGAuXG4gICAqL1xuICBwcml2YXRlIGZpbmRTaGFyZEZvckJ5dGUoYnl0ZUluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGlmICh0aGlzLnNoYXJkcy5sZW5ndGggPT09IDAgfHwgYnl0ZUluZGV4IDwgMCB8fFxuICAgICAgYnl0ZUluZGV4ID49IHRoaXMuYnl0ZUxlbmd0aCkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cblxuICAgIC8vIElmIHRoZSBidWZmZXJzIGhhdmUgYSB1bmlmb3JtIHNpemUsIGNvbXB1dGUgdGhlIHNoYXJkIGRpcmVjdGx5LlxuICAgIGlmICh0aGlzLmJ1ZmZlclVuaWZvcm1TaXplICE9IG51bGwpIHtcbiAgICAgIHRoaXMucHJldmlvdXNTaGFyZEluZGV4ID0gTWF0aC5mbG9vcihieXRlSW5kZXggLyB0aGlzLmJ1ZmZlclVuaWZvcm1TaXplKTtcbiAgICAgIHJldHVybiB0aGlzLnByZXZpb3VzU2hhcmRJbmRleDtcbiAgICB9XG5cbiAgICAvLyBJZiB0aGUgYnVmZmVycyBkb24ndCBoYXZlIGEgdW5pZm9ybSBzaXplLCB3ZSBuZWVkIHRvIHNlYXJjaCBmb3IgdGhlXG4gICAgLy8gc2hhcmQuIFRoYXQgbWVhbnMgd2UgbmVlZCBhIGZ1bmN0aW9uIHRvIGNoZWNrIHdoZXJlIHRoZSBieXRlSW5kZXggbGllc1xuICAgIC8vIHJlbGF0aXZlIHRvIGEgZ2l2ZW4gc2hhcmQuXG4gICAgZnVuY3Rpb24gY2hlY2soc2hhcmQ6IEJ1ZmZlclNoYXJkKSB7XG4gICAgICBpZiAoYnl0ZUluZGV4IDwgc2hhcmQuc3RhcnQpIHtcbiAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgfVxuICAgICAgaWYgKGJ5dGVJbmRleCA+PSBzaGFyZC5lbmQpIHtcbiAgICAgICAgcmV0dXJuIDE7XG4gICAgICB9XG4gICAgICByZXR1cm4gMDtcbiAgICB9XG5cbiAgICAvLyBGb3IgZWZmaWNpZW5jeSwgdHJ5IHRoZSBwcmV2aW91cyBzaGFyZCBmaXJzdC5cbiAgICBpZiAoY2hlY2sodGhpcy5zaGFyZHNbdGhpcy5wcmV2aW91c1NoYXJkSW5kZXhdKSA9PT0gMCkge1xuICAgICAgcmV0dXJuIHRoaXMucHJldmlvdXNTaGFyZEluZGV4O1xuICAgIH1cblxuICAgIC8vIE90aGVyd2lzZSwgdXNlIGEgZ2VuZXJpYyBzZWFyY2ggZnVuY3Rpb24uXG4gICAgLy8gVGhpcyBzaG91bGQgYWxtb3N0IG5ldmVyIGVuZCB1cCBiZWluZyB1c2VkIGluIHByYWN0aWNlIHNpbmNlIHRoZSB3ZWlnaHRcbiAgICAvLyBlbnRyaWVzIHNob3VsZCBhbHdheXMgYmUgaW4gb3JkZXIuXG4gICAgY29uc3QgaW5kZXggPSBzZWFyY2godGhpcy5zaGFyZHMsIGNoZWNrKTtcbiAgICBpZiAoaW5kZXggPT09IC0xKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuXG4gICAgdGhpcy5wcmV2aW91c1NoYXJkSW5kZXggPSBpbmRleDtcbiAgICByZXR1cm4gdGhpcy5wcmV2aW91c1NoYXJkSW5kZXg7XG4gIH1cbn1cblxuLyoqXG4gKiBTZWFyY2ggZm9yIGFuIGVsZW1lbnQgb2YgYSBzb3J0ZWQgYXJyYXkuXG4gKlxuICogQHBhcmFtIHNvcnRlZEFycmF5IFRoZSBzb3J0ZWQgYXJyYXkgdG8gc2VhcmNoXG4gKiBAcGFyYW0gY29tcGFyZSBBIGZ1bmN0aW9uIHRvIGNvbXBhcmUgdGhlIGN1cnJlbnQgdmFsdWUgYWdhaW5zdCB0aGUgc2VhcmNoZWRcbiAqICAgICB2YWx1ZS4gUmV0dXJuIDAgb24gYSBtYXRjaCwgbmVnYXRpdmUgaWYgdGhlIHNlYXJjaGVkIHZhbHVlIGlzIGxlc3MgdGhhblxuICogICAgIHRoZSB2YWx1ZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLCBhbmQgcG9zaXRpdmUgaWYgdGhlIHNlYXJjaGVkIHZhbHVlIGlzXG4gKiAgICAgZ3JlYXRlciB0aGFuIHRoZSB2YWx1ZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLlxuICogQHJldHVybnMgVGhlIGluZGV4IG9mIHRoZSBlbGVtZW50LCBvciAtMSBpZiBpdCdzIG5vdCBpbiB0aGUgYXJyYXkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZWFyY2g8VD4oc29ydGVkQXJyYXk6IFRbXSwgY29tcGFyZTogKHQ6IFQpID0+IG51bWJlcik6IG51bWJlciB7XG4gIC8vIEJpbmFyeSBzZWFyY2hcbiAgbGV0IG1pbiA9IDA7XG4gIGxldCBtYXggPSBzb3J0ZWRBcnJheS5sZW5ndGg7XG5cbiAgd2hpbGUgKG1pbiA8PSBtYXgpIHtcbiAgICBjb25zdCBtaWRkbGUgPSBNYXRoLmZsb29yKChtYXggLSBtaW4pIC8gMikgKyBtaW47XG4gICAgY29uc3Qgc2lkZSA9IGNvbXBhcmUoc29ydGVkQXJyYXlbbWlkZGxlXSk7XG5cbiAgICBpZiAoc2lkZSA9PT0gMCkge1xuICAgICAgcmV0dXJuIG1pZGRsZTtcbiAgICB9IGVsc2UgaWYgKHNpZGUgPCAwKSB7XG4gICAgICBtYXggPSBtaWRkbGU7XG4gICAgfSBlbHNlIHtcbiAgICAgIG1pbiA9IG1pZGRsZSArIDE7XG4gICAgfVxuICB9XG4gIHJldHVybiAtMTtcbn1cbiJdfQ==