/// <summary> /// Creates a sparse array from the given dense array. /// </summary> /// <param name="array"> The array to copy items from. </param> /// <param name="length"> The number of items to copy. </param> /// <returns> A new sparse array containing the items from the given array. </returns> public static SparseArray FromDenseArray(object[] array, int length) { if (array == null) throw new ArgumentNullException("array"); if (length > array.Length) throw new ArgumentOutOfRangeException("length"); var result = new SparseArray(); result.CopyTo(array, 0, length); return result; }
/// <summary> /// Creates a new array and initializes it with the given sparse array. /// </summary> /// <param name="prototype"> The next object in the prototype chain. </param> /// <param name="sparseArray"> The sparse array to use as the backing store. </param> /// <param name="length"> The initial value of the length property. </param> private ArrayInstance(ObjectInstance prototype, SparseArray sparseArray, uint length) : base(prototype) { if (sparseArray == null) throw new ArgumentNullException("sparseArray"); this.sparse = sparseArray; // Create a fake property for length plus initialize the real length property. this.length = length; FastSetProperty("length", -1, PropertyAttributes.Writable | PropertyAttributes.IsLengthProperty); }
// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a new array with the given length and capacity. /// </summary> /// <param name="prototype"> The next object in the prototype chain. </param> /// <param name="length"> The initial value of the length property. </param> /// <param name="capacity"> The number of elements to allocate. </param> internal ArrayInstance(ObjectInstance prototype, uint length, uint capacity) : base(prototype) { if (length > capacity) throw new ArgumentOutOfRangeException("length", "length must be less than or equal to capacity."); if (length <= 1000) { this.dense = new object[(int)capacity]; this.denseMayContainHoles = length > 0; } else { this.sparse = new SparseArray(); } // Create a fake property for length plus initialize the real length property. this.length = length; FastSetProperty("length", -1, PropertyAttributes.Writable | PropertyAttributes.IsLengthProperty); }
public static ArrayInstance Concat(ObjectInstance thisObj, params object[] items) { // Create a new items array with the thisObject at the beginning. var temp = new object[items.Length + 1]; temp[0] = thisObj; Array.Copy(items, 0, temp, 1, items.Length); items = temp; // Determine if the resulting array should be dense or sparse and calculate the length // at the same time. bool dense = true; uint length = (uint)items.Length; foreach (object item in items) if (item is ArrayInstance) { length += ((ArrayInstance)item).Length - 1; if (((ArrayInstance)item).dense == null) dense = false; } // This method only supports arrays of length up to 2^31-1, rather than 2^32-1. if (length > int.MaxValue) throw new JavaScriptException(thisObj.Engine, "RangeError", "The resulting array is too long"); if (dense == true) { // Create a dense array. var result = new object[length]; int index = 0; foreach (object item in items) { if (item is ArrayInstance) { // Add the items in the array to the end of the resulting array. var array = (ArrayInstance)item; Array.Copy(array.dense, 0, result, index, (int)array.Length); if (array.denseMayContainHoles == true && array.Prototype != null) { // Populate holes from the prototype. for (uint i = 0; i < array.length; i++) if (array.dense[i] == null) result[index + i] = array.Prototype.GetPropertyValue(i); } index += (int)array.Length; } else { // Add the item to the end of the resulting array. result[index ++] = item; } } // Return the new dense array. return new ArrayInstance(thisObj.Engine.Array.InstancePrototype, result); } else { // Create a new sparse array. var result = new SparseArray(); int index = 0; foreach (object item in items) { if (item is ArrayInstance) { // Add the items in the array to the end of the resulting array. var array = (ArrayInstance)item; if (array.dense != null) { result.CopyTo(array.dense, (uint)index, (int)array.Length); if (array.Prototype != null) { // Populate holes from the prototype. for (uint i = 0; i < array.length; i++) if (array.dense[i] == null) result[(uint)index + i] = array.Prototype.GetPropertyValue(i); } } else { result.CopyTo(array.sparse, (uint)index); if (array.Prototype != null) { // Populate holes from the prototype. for (uint i = 0; i < array.Length; i++) if (array.sparse[i] == null) result[(uint)index + i] = array.Prototype.GetPropertyValue(i); } } index += (int)array.Length; } else { // Add the item to the end of the resulting array. result[(uint)index] = item; index++; } } // Return the new sparse array. return new ArrayInstance(thisObj.Engine.Array.InstancePrototype, result, length); } }
/// <summary> /// Sets the value of the property with the given array index. If a property with the /// given index does not exist, or exists in the prototype chain (and is not a setter) then /// a new property is created. /// </summary> /// <param name="index"> The array index of the property to set. </param> /// <param name="value"> The value to set the property to. This must be a javascript /// primitive (double, string, etc) or a class derived from <see cref="ObjectInstance"/>. </param> /// <param name="throwOnError"> <c>true</c> to throw an exception if the property could not /// be set. This can happen if the property is read-only or if the object is sealed. </param> public override void SetPropertyValue(uint index, object value, bool throwOnError) { value = value ?? Undefined.Value; if (this.dense != null) { if (index < this.length) { // The index is inside the existing bounds of the array. this.dense[index] = value; } else if (index < this.dense.Length) { // The index is outside the bounds of the array but inside the allocated buffer. this.dense[index] = value; this.denseMayContainHoles = this.denseMayContainHoles || index > this.length; this.length = index + 1; } else { // The index is out of range - either enlarge the array or switch to sparse. if (index < this.dense.Length + 10) { // Enlarge the dense array. ResizeDenseArray((uint)(this.dense.Length * 2 + 10), this.length); // Set the value. this.dense[index] = value; this.denseMayContainHoles = this.denseMayContainHoles || index > this.length; } else { // Switch to a sparse array. this.sparse = SparseArray.FromDenseArray(this.dense, (int)this.length); this.dense = null; this.sparse[index] = value; } // Update the length. this.length = index + 1; } } else { // Set the value and update the length. this.sparse[index] = value; this.length = Math.Max(this.length, index + 1); } }
/// <summary> /// Copies the elements of the given sparse array to this sparse array, starting at a /// particular index. Existing values are overwritten. /// </summary> /// <param name="source"> The sparse array to copy. </param> /// <param name="start"> The zero-based index at which copying begins. </param> public void CopyTo(SparseArray source, uint start) { foreach (var sourceRange in source.Ranges) { int sourceOffset = 0; uint destIndex = start + sourceRange.StartIndex; do { // Get a reference to the array to copy to. object[] dest = FindOrCreateArray(start, writeAccess: true); int destOffset = (int)(destIndex & NodeMask); // Copy as much as possible. int copyLength = Math.Min(sourceRange.Length - sourceOffset, dest.Length - destOffset); Array.Copy(sourceRange.Array, sourceOffset, dest, destOffset, copyLength); start += (uint)copyLength; sourceOffset += copyLength; } while (sourceOffset < sourceRange.Length); } }