/** * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ var SequenceModule = require('./Sequence'); var Sequence = SequenceModule.Sequence; var IndexedSequence = SequenceModule.IndexedSequence; var ImmutableMap = require('./Map'); for(var IndexedSequence____Key in IndexedSequence){if(IndexedSequence.hasOwnProperty(IndexedSequence____Key)){Vector[IndexedSequence____Key]=IndexedSequence[IndexedSequence____Key];}}var ____SuperProtoOfIndexedSequence=IndexedSequence===null?null:IndexedSequence.prototype;Vector.prototype=Object.create(____SuperProtoOfIndexedSequence);Vector.prototype.constructor=Vector;Vector.__superConstructor__=IndexedSequence; // @pragma Construction function Vector() {"use strict";var values=Array.prototype.slice.call(arguments,0); return Vector.from(values); } Vector.empty=function() {"use strict"; return __EMPTY_VECT || (__EMPTY_VECT = Vector.$Vector_make(0, 0, SHIFT, __EMPTY_VNODE, __EMPTY_VNODE) ); }; Vector.from=function(sequence) {"use strict"; if (sequence && sequence.constructor === Vector) { return sequence; } if (!sequence || sequence.length === 0) { return Vector.empty(); } var isArray = Array.isArray(sequence); if (sequence.length > 0 && sequence.length < SIZE) { return Vector.$Vector_make(0, sequence.length, SHIFT, __EMPTY_VNODE, new VNode( isArray ? sequence.slice() : Sequence(sequence).toArray() )); } if (!isArray) { sequence = Sequence(sequence); if (!(sequence instanceof IndexedSequence)) { sequence = sequence.values(); } } return Vector.empty().merge(sequence); }; Vector.prototype.toString=function() {"use strict"; return this.__toString('Vector [', ']'); }; // @pragma Access Vector.prototype.get=function(index, undefinedValue) {"use strict"; index = rawIndex(index, this.$Vector_origin); if (index >= this.$Vector_size) { return undefinedValue; } var node = this.$Vector_nodeFor(index); var maskedIndex = index & MASK; return node && (undefinedValue === undefined || node.array.hasOwnProperty(maskedIndex)) ? node.array[maskedIndex] : undefinedValue; }; Vector.prototype.first=function() {"use strict"; return this.get(0); }; Vector.prototype.last=function() {"use strict"; return this.get(this.length ? this.length - 1 : 0); }; // @pragma Modification // TODO: set and delete seem very similar. Vector.prototype.set=function(index, value) {"use strict"; var tailOffset = getTailOffset(this.$Vector_size); if (index >= this.length) { return this.withMutations(function(vect) {return vect.$Vector_setBounds(0, index + 1).set(index, value);} ); } if (this.get(index, __SENTINEL) === value) { return this; } index = rawIndex(index, this.$Vector_origin); // Fits within tail. if (index >= tailOffset) { var newTail = this.$Vector_tail.ensureOwner(this.__ownerID); newTail.array[index & MASK] = value; var newSize = index >= this.$Vector_size ? index + 1 : this.$Vector_size; if (this.__ownerID) { this.length = newSize - this.$Vector_origin; this.$Vector_size = newSize; this.$Vector_tail = newTail; return this; } return Vector.$Vector_make(this.$Vector_origin, newSize, this.$Vector_level, this.$Vector_root, newTail); } // Fits within existing tree. var newRoot = this.$Vector_root.ensureOwner(this.__ownerID); var node = newRoot; for (var level = this.$Vector_level; level > 0; level -= SHIFT) { var idx = (index >>> level) & MASK; node = node.array[idx] = node.array[idx] ? node.array[idx].ensureOwner(this.__ownerID) : new VNode([], this.__ownerID); } node.array[index & MASK] = value; if (this.__ownerID) { this.$Vector_root = newRoot; return this; } return Vector.$Vector_make(this.$Vector_origin, this.$Vector_size, this.$Vector_level, newRoot, this.$Vector_tail); }; Vector.prototype.delete=function(index) {"use strict"; // Out of bounds, no-op. Probably a more efficient way to do this... if (!this.has(index)) { return this; } var tailOffset = getTailOffset(this.$Vector_size); index = rawIndex(index, this.$Vector_origin); // Delete within tail. if (index >= tailOffset) { var newTail = this.$Vector_tail.ensureOwner(this.__ownerID); delete newTail.array[index & MASK]; if (this.__ownerID) { this.$Vector_tail = newTail; return this; } return Vector.$Vector_make(this.$Vector_origin, this.$Vector_size, this.$Vector_level, this.$Vector_root, newTail); } // Fits within existing tree. var newRoot = this.$Vector_root.ensureOwner(this.__ownerID); var node = newRoot; for (var level = this.$Vector_level; level > 0; level -= SHIFT) { var idx = (index >>> level) & MASK; // TODO: if we don't check "has" above, this could be null. node = node.array[idx] = node.array[idx].ensureOwner(this.__ownerID); } delete node.array[index & MASK]; if (this.__ownerID) { this.$Vector_root = newRoot; return this; } return Vector.$Vector_make(this.$Vector_origin, this.$Vector_size, this.$Vector_level, newRoot, this.$Vector_tail); }; Vector.prototype.clear=function() {"use strict"; if (this.__ownerID) { this.length = this.$Vector_origin = this.$Vector_size = 0; this.$Vector_level = SHIFT; this.$Vector_root = this.$Vector_tail = __EMPTY_VNODE; return this; } return Vector.empty(); }; Vector.prototype.push=function() {"use strict"; var values = arguments; var oldLength = this.length; return this.withMutations(function(vect) { vect.$Vector_setBounds(0, oldLength + values.length); for (var ii = 0; ii < values.length; ii++) { vect.set(oldLength + ii, values[ii]); } }); }; Vector.prototype.pop=function() {"use strict"; return this.$Vector_setBounds(0, -1); }; Vector.prototype.unshift=function() {"use strict"; var values = arguments; return this.withMutations(function(vect) { vect.$Vector_setBounds(-values.length); for (var ii = 0; ii < values.length; ii++) { vect.set(ii, values[ii]); } }); }; Vector.prototype.shift=function() {"use strict"; return this.$Vector_setBounds(1); }; // @pragma Composition Vector.prototype.merge=function() {"use strict";var seqs=Array.prototype.slice.call(arguments,0); return ImmutableMap.prototype.merge.apply( vectorWithLengthOfLongestSeq(this, seqs), arguments); }; Vector.prototype.mergeWith=function(fn) {"use strict";var seqs=Array.prototype.slice.call(arguments,1); return ImmutableMap.prototype.mergeWith.apply( vectorWithLengthOfLongestSeq(this, seqs), arguments); }; Vector.prototype.mergeDeep=function() {"use strict";var seqs=Array.prototype.slice.call(arguments,0); return ImmutableMap.prototype.mergeDeep.apply( vectorWithLengthOfLongestSeq(this, seqs), arguments); }; Vector.prototype.mergeDeepWith=function(fn) {"use strict";var seqs=Array.prototype.slice.call(arguments,1); return ImmutableMap.prototype.mergeDeepWith.apply( vectorWithLengthOfLongestSeq(this, seqs), arguments); }; Vector.prototype.setLength=function(length) {"use strict"; return this.$Vector_setBounds(0, length); }; Vector.prototype.$Vector_setBounds=function(begin, end) {"use strict"; var owner = this.__ownerID || new OwnerID(); var oldOrigin = this.$Vector_origin; var oldSize = this.$Vector_size; var newOrigin = oldOrigin + begin; var newSize = end == null ? oldSize : end < 0 ? oldSize + end : oldOrigin + end; if (newOrigin === oldOrigin && newSize === oldSize) { return this; } // If it's going to end after it starts, it's empty. if (newOrigin >= newSize) { return this.clear(); } var newLevel = this.$Vector_level; var newRoot = this.$Vector_root; // New origin might require creating a higher root. var offsetShift = 0; while (newOrigin + offsetShift < 0) { // TODO: why only ever shifting over by 1? newRoot = new VNode(newRoot.array.length ? [,newRoot] : [], owner); offsetShift += 1 << newLevel; newLevel += SHIFT; } if (offsetShift) { newOrigin += offsetShift; oldOrigin += offsetShift; newSize += offsetShift; oldSize += offsetShift; } var oldTailOffset = getTailOffset(oldSize); var newTailOffset = getTailOffset(newSize); // New size might require creating a higher root. while (newTailOffset >= 1 << (newLevel + SHIFT)) { newRoot = new VNode(newRoot.array.length ? [newRoot] : [], owner); newLevel += SHIFT; } // Locate or create the new tail. var oldTail = this.$Vector_tail; var newTail = newTailOffset < oldTailOffset ? this.$Vector_nodeFor(newSize) : newTailOffset > oldTailOffset ? new VNode([], owner) : oldTail; // Merge Tail into tree. if (newTailOffset > oldTailOffset && newOrigin < oldSize && oldTail.array.length) { newRoot = newRoot.ensureOwner(owner); var node = newRoot; for (var level = newLevel; level > SHIFT; level -= SHIFT) { var idx = (oldTailOffset >>> level) & MASK; node = node.array[idx] = node.array[idx] ? node.array[idx].ensureOwner(owner) : new VNode([], owner); } node.array[(oldTailOffset >>> SHIFT) & MASK] = oldTail; } // If the size has been reduced, there's a chance the tail needs to be trimmed. if (newSize < oldSize) { newTail = newTail.removeAfter(owner, 0, newSize); } // If the new origin is within the tail, then we do not need a root. if (newOrigin >= newTailOffset) { newOrigin -= newTailOffset; newSize -= newTailOffset; newLevel = SHIFT; newRoot = __EMPTY_VNODE; newTail = newTail.removeBefore(owner, 0, newOrigin); // Otherwise, if the root has been trimmed, garbage collect. } else if (newOrigin > oldOrigin || newTailOffset < oldTailOffset) { var beginIndex, endIndex; offsetShift = 0; // Identify the new top root node of the subtree of the old root. do { beginIndex = ((newOrigin) >>> newLevel) & MASK; endIndex = ((newTailOffset - 1) >>> newLevel) & MASK; if (beginIndex === endIndex) { if (beginIndex) { offsetShift += (1 << newLevel) * beginIndex; } newLevel -= SHIFT; newRoot = newRoot && newRoot.array[beginIndex]; } } while (newRoot && beginIndex === endIndex); // Trim the new sides of the new root. if (newRoot && newOrigin > oldOrigin) { newRoot = newRoot.removeBefore(owner, newLevel, newOrigin - offsetShift); } if (newRoot && newTailOffset < oldTailOffset) { newRoot = newRoot.removeAfter(owner, newLevel, newTailOffset - offsetShift); } if (offsetShift) { newOrigin -= offsetShift; newSize -= offsetShift; } // Ensure root is not null. newRoot = newRoot || __EMPTY_VNODE; } if (this.__ownerID) { this.length = newSize - newOrigin; this.$Vector_origin = newOrigin; this.$Vector_size = newSize; this.$Vector_level = newLevel; this.$Vector_root = newRoot; this.$Vector_tail = newTail; return this; } return Vector.$Vector_make(newOrigin, newSize, newLevel, newRoot, newTail); }; // @pragma Mutability Vector.prototype.__ensureOwner=function(ownerID) {"use strict"; if (ownerID === this.__ownerID) { return this; } if (!ownerID) { this.__ownerID = ownerID; return this; } return Vector.$Vector_make(this.$Vector_origin, this.$Vector_size, this.$Vector_level, this.$Vector_root, this.$Vector_tail, ownerID); }; // @pragma Iteration Vector.prototype.slice=function(begin, end, maintainIndices) {"use strict"; var sliceSequence = ____SuperProtoOfIndexedSequence.slice.call(this,begin, end, maintainIndices); // Optimize the case of vector.slice(b, e).toVector() if (!maintainIndices && sliceSequence !== this) { var vector = this; var length = vector.length; sliceSequence.toVector = function() {return vector.$Vector_setBounds( begin < 0 ? Math.max(0, length + begin) : length ? Math.min(length, begin) : begin, end == null ? length : end < 0 ? Math.max(0, length + end) : length ? Math.min(length, end) : end );}; } return sliceSequence; }; Vector.prototype.__deepEquals=function(other) {"use strict"; var is = require('./Immutable').is; var iterator = this.__iterator__(); return other.every(function(v, k) { var entry = iterator.next(); return k === entry[0] && is(v, entry[1]); }); }; Vector.prototype.__iterator__=function() {"use strict"; return new VectorIterator( this, this.$Vector_origin, this.$Vector_size, this.$Vector_level, this.$Vector_root, this.$Vector_tail ); }; Vector.prototype.__iterate=function(fn, reverse, flipIndices) {"use strict"; var vector = this; var lastIndex = 0; var maxIndex = vector.length - 1; flipIndices ^= reverse; var eachFn = function(value, ii) { if (fn(value, flipIndices ? maxIndex - ii : ii, vector) === false) { return false; } else { lastIndex = ii; return true; } }; var didComplete; var tailOffset = getTailOffset(this.$Vector_size); if (reverse) { didComplete = this.$Vector_tail.iterate(0, tailOffset - this.$Vector_origin, this.$Vector_size - this.$Vector_origin, eachFn, reverse) && this.$Vector_root.iterate(this.$Vector_level, -this.$Vector_origin, tailOffset - this.$Vector_origin, eachFn, reverse); } else { didComplete = this.$Vector_root.iterate(this.$Vector_level, -this.$Vector_origin, tailOffset - this.$Vector_origin, eachFn, reverse) && this.$Vector_tail.iterate(0, tailOffset - this.$Vector_origin, this.$Vector_size - this.$Vector_origin, eachFn, reverse); } return (didComplete ? maxIndex : reverse ? maxIndex - lastIndex : lastIndex) + 1; }; // @pragma Private Vector.$Vector_make=function(origin, size, level, root, tail, ownerID) {"use strict"; var vect = Object.create(Vector.prototype); vect.length = size - origin; vect.$Vector_origin = origin; vect.$Vector_size = size; vect.$Vector_level = level; vect.$Vector_root = root; vect.$Vector_tail = tail; vect.__ownerID = ownerID; return vect; }; Vector.prototype.$Vector_nodeFor=function(rawIndex) {"use strict"; if (rawIndex >= getTailOffset(this.$Vector_size)) { return this.$Vector_tail; } if (rawIndex < 1 << (this.$Vector_level + SHIFT)) { var node = this.$Vector_root; var level = this.$Vector_level; while (node && level > 0) { node = node.array[(rawIndex >>> level) & MASK]; level -= SHIFT; } return node; } }; Vector.prototype.withMutations = ImmutableMap.prototype.withMutations; Vector.prototype.updateIn = ImmutableMap.prototype.updateIn; function OwnerID() {"use strict";} function VNode(array, ownerID) {"use strict"; this.array = array; this.ownerID = ownerID; } VNode.prototype.ensureOwner=function(ownerID) {"use strict"; if (ownerID && ownerID === this.ownerID) { return this; } return new VNode(this.array.slice(), ownerID); }; // TODO: seems like these methods are very similar VNode.prototype.removeBefore=function(ownerID, level, index) {"use strict"; if (index === 1 << level || this.array.length === 0) { return this; } var originIndex = (index >>> level) & MASK; if (originIndex >= this.array.length) { return new VNode([], ownerID); } var removingFirst = originIndex === 0; var newChild; if (level > 0) { var oldChild = this.array[originIndex]; newChild = oldChild && oldChild.removeBefore(ownerID, level - SHIFT, index); if (newChild === oldChild && removingFirst) { return this; } } if (removingFirst && !newChild) { return this; } var editable = this.ensureOwner(); if (!removingFirst) { for (var ii = 0; ii < originIndex; ii++) { delete editable.array[ii]; } } if (newChild) { editable.array[originIndex] = newChild; } return editable; }; VNode.prototype.removeAfter=function(ownerID, level, index) {"use strict"; if (index === 1 << level || this.array.length === 0) { return this; } var sizeIndex = ((index - 1) >>> level) & MASK; if (sizeIndex >= this.array.length) { return this; } var removingLast = sizeIndex === this.array.length - 1; var newChild; if (level > 0) { var oldChild = this.array[sizeIndex]; newChild = oldChild && oldChild.removeAfter(ownerID, level - SHIFT, index); if (newChild === oldChild && removingLast) { return this; } } if (removingLast && !newChild) { return this; } var editable = this.ensureOwner(); if (!removingLast) { editable.array.length = sizeIndex + 1; } if (newChild) { editable.array[sizeIndex] = newChild; } return editable; }; VNode.prototype.iterate=function(level, offset, max, fn, reverse) {"use strict"; // Note using every() gets us a speed-up of 2x on modern JS VMs, but means // we cannot support IE8 without polyfill. if (level === 0) { if (reverse) { for (var revRawIndex = this.array.length - 1; revRawIndex >= 0; revRawIndex--) { if (this.array.hasOwnProperty(revRawIndex)) { var index = revRawIndex + offset; if (index >= 0 && index < max && fn(this.array[revRawIndex], index) === false) { return false; } } } return true; } else { return this.array.every(function(value, rawIndex) { var index = rawIndex + offset; return index < 0 || index >= max || fn(value, index) !== false; }); } } var step = 1 << level; var newLevel = level - SHIFT; if (reverse) { for (var revLevelIndex = this.array.length - 1; revLevelIndex >= 0; revLevelIndex--) { var newOffset = offset + revLevelIndex * step; if (newOffset < max && newOffset + step > 0 && this.array.hasOwnProperty(revLevelIndex) && !this.array[revLevelIndex].iterate(newLevel, newOffset, max, fn, reverse)) { return false; } } return true; } else { return this.array.every(function(newNode, levelIndex) { var newOffset = offset + levelIndex * step; return newOffset >= max || newOffset + step <= 0 || newNode.iterate(newLevel, newOffset, max, fn, reverse); }); } }; function VectorIterator(vector, origin, size, level, root, tail) {"use strict"; var tailOffset = getTailOffset(size); this.$VectorIterator_stack = { node: root.array, level: level, offset: -origin, max: tailOffset - origin, __prev: { node: tail.array, level: 0, offset: tailOffset - origin, max: size - origin } }; } VectorIterator.prototype.next=function() {"use strict"; var stack = this.$VectorIterator_stack; iteration: while (stack) { if (stack.level === 0) { stack.rawIndex || (stack.rawIndex = 0); while (stack.rawIndex < stack.node.length) { var index = stack.rawIndex + stack.offset; if (index >= 0 && index < stack.max && stack.node.hasOwnProperty(stack.rawIndex)) { var value = stack.node[stack.rawIndex]; stack.rawIndex++; return [index, value]; } else { stack.rawIndex++; } } } else { var step = 1 << stack.level; stack.levelIndex || (stack.levelIndex = 0); while (stack.levelIndex < stack.node.length) { var newOffset = stack.offset + stack.levelIndex * step; if (newOffset + step > 0 && newOffset < stack.max && stack.node.hasOwnProperty(stack.levelIndex)) { var newNode = stack.node[stack.levelIndex].array; stack.levelIndex++; stack = this.$VectorIterator_stack = { node: newNode, level: stack.level - SHIFT, offset: newOffset, max: stack.max, __prev: stack }; continue iteration; } else { stack.levelIndex++; } } } stack = this.$VectorIterator_stack = this.$VectorIterator_stack.__prev; } if (global.StopIteration) { throw global.StopIteration; } }; function vectorWithLengthOfLongestSeq(vector, seqs) { var maxLength = Math.max.apply(null, seqs.map(function(seq) {return seq.length || 0;})); return maxLength > vector.length ? vector.setLength(maxLength) : vector; } function rawIndex(index, origin) { if (index < 0) throw new Error('Index out of bounds'); return index + origin; } function getTailOffset(size) { return size < SIZE ? 0 : (((size - 1) >>> SHIFT) << SHIFT); } var SHIFT = 5; // Resulted in best performance after ______? var SIZE = 1 << SHIFT; var MASK = SIZE - 1; var __SENTINEL = {}; var __EMPTY_VECT; var __EMPTY_VNODE = new VNode([]); module.exports = Vector;