| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 /* | 
|  | 2  * This file is part of Adblock Plus <http://adblockplus.org/>, | 
|  | 3  * Copyright (C) 2006-2014 Eyeo GmbH | 
|  | 4  * | 
|  | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|  | 6  * it under the terms of the GNU General Public License version 3 as | 
|  | 7  * published by the Free Software Foundation. | 
|  | 8  * | 
|  | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
|  | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12  * GNU General Public License for more details. | 
|  | 13  * | 
|  | 14  * You should have received a copy of the GNU General Public License | 
|  | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 16  */ | 
|  | 17 | 
|  | 18 "use strict"; | 
|  | 19 | 
|  | 20 let {ilog2, nextPow2, alloc, dealloc, addBuffer, removeBuffer} = require("typedO
     bjects/utils"); | 
|  | 21 | 
|  | 22 function defaultArrayConstructor() | 
|  | 23 { | 
|  | 24   this.arrayBufferIndex = -1; | 
|  | 25 }; | 
|  | 26 | 
|  | 27 function defaultArrayDestructor() | 
|  | 28 { | 
|  | 29   this.length = 0; | 
|  | 30   this.size = 0; | 
|  | 31 }; | 
|  | 32 | 
|  | 33 function createGetter(elementGetter, elementShift) | 
|  | 34 { | 
|  | 35   return function(index) | 
|  | 36   { | 
|  | 37     if (index < 0 || index >= this.length) | 
|  | 38       throw new Error("Array index out of bounds"); | 
|  | 39     return elementGetter.call(this, this.arrayBufferIndex, this.arrayByteOffset 
     + (index << elementShift)); | 
|  | 40   }; | 
|  | 41 } | 
|  | 42 | 
|  | 43 function createSetter(elementSetter, elementShift) | 
|  | 44 { | 
|  | 45   return function(index, value) | 
|  | 46   { | 
|  | 47     if (index < 0 || index >= this.length) | 
|  | 48       throw new Error("Array index out of bounds"); | 
|  | 49     return elementSetter.call(this, this.arrayBufferIndex, this.arrayByteOffset 
     + (index << elementShift), value); | 
|  | 50   } | 
|  | 51 } | 
|  | 52 | 
|  | 53 function createCombinedConstructor(customConstructor) | 
|  | 54 { | 
|  | 55   return function() | 
|  | 56   { | 
|  | 57     defaultArrayConstructor.apply(this); | 
|  | 58     customConstructor.apply(this, arguments); | 
|  | 59   } | 
|  | 60 } | 
|  | 61 | 
|  | 62 function createCombinedDestructor(customDestructor) | 
|  | 63 { | 
|  | 64   return function() | 
|  | 65   { | 
|  | 66     try | 
|  | 67     { | 
|  | 68       customDestructor.apply(this); | 
|  | 69     } | 
|  | 70     finally | 
|  | 71     { | 
|  | 72       defaultArrayDestructor.apply(this); | 
|  | 73     } | 
|  | 74   } | 
|  | 75 } | 
|  | 76 | 
|  | 77 function createLengthWatcher(elementType, elementSetter) | 
|  | 78 { | 
|  | 79   let {STATE_UNINITIALIZED} = require("typedObjects/objectTypes"); | 
|  | 80   return function lengthWatcher(newLength) | 
|  | 81   { | 
|  | 82     newLength = newLength | 0; | 
|  | 83     if (newLength < 0) | 
|  | 84       newLength = 0; | 
|  | 85     if (newLength > this.size) | 
|  | 86       this.size = newLength; | 
|  | 87 | 
|  | 88     let cleanupValue = elementType.cleanupValue; | 
|  | 89     if (typeof cleanupValue != "undefined") | 
|  | 90     { | 
|  | 91       let length = this.length; | 
|  | 92       if (newLength > length) | 
|  | 93       { | 
|  | 94         // We have to call element setter directly here, this.set() will | 
|  | 95         // complain because of writing out of bounds (new length isn't set yet). | 
|  | 96         // We also need to change state temporarily in order to avoid an attemt | 
|  | 97         // to release "existing" values. | 
|  | 98         let origState = this._state; | 
|  | 99         this._state = STATE_UNINITIALIZED; | 
|  | 100         try | 
|  | 101         { | 
|  | 102           let referenceLength = elementType.referenceLength | 0; | 
|  | 103           let bufferIndex = this.arrayBufferIndex | 0; | 
|  | 104           for (let i = length, offset = this.arrayByteOffset + length * referenc
     eLength; | 
|  | 105               i < newLength; | 
|  | 106               i++, offset += referenceLength) | 
|  | 107           { | 
|  | 108             elementSetter.call(this, bufferIndex, offset, cleanupValue); | 
|  | 109           } | 
|  | 110         } | 
|  | 111         finally | 
|  | 112         { | 
|  | 113           this._state = origState; | 
|  | 114         } | 
|  | 115       } | 
|  | 116       else | 
|  | 117       { | 
|  | 118         for (let i = newLength; i < length; i++) | 
|  | 119           this.set(i, cleanupValue); | 
|  | 120       } | 
|  | 121     } | 
|  | 122 | 
|  | 123     return newLength; | 
|  | 124   } | 
|  | 125 } | 
|  | 126 | 
|  | 127 function createSizeWatcher(elementType, minElements, bufferSize, buffers, viewTy
     pes, views, firstFree) | 
|  | 128 { | 
|  | 129   let referenceLength = elementType.referenceLength | 0; | 
|  | 130   minElements = minElements | 0; | 
|  | 131   bufferSize = bufferSize | 0; | 
|  | 132   return function sizeWatcher(newSize) | 
|  | 133   { | 
|  | 134     newSize = newSize | 0; | 
|  | 135     let length = this.length | 0; | 
|  | 136     if (newSize < length) | 
|  | 137       newSize = length; | 
|  | 138     if (newSize > 0 && newSize < minElements) | 
|  | 139       newSize = minElements; | 
|  | 140     newSize = nextPow2(newSize); | 
|  | 141 | 
|  | 142     let size = this.size; | 
|  | 143     if (size != newSize) | 
|  | 144     { | 
|  | 145       let origBufferIndex = this.arrayBufferIndex; | 
|  | 146       let origByteOffset = this.arrayByteOffset; | 
|  | 147       if (newSize > 0) | 
|  | 148       { | 
|  | 149         // Allocate new buffer | 
|  | 150         let bufferIndex, byteOffset; | 
|  | 151         let reference = firstFree[newSize]; | 
|  | 152         if (typeof reference != "undefined") | 
|  | 153         { | 
|  | 154           [bufferIndex, byteOffset] = alloc(reference, | 
|  | 155               referenceLength * newSize, (bufferSize / newSize) | 0, | 
|  | 156               buffers, viewTypes, views); | 
|  | 157         } | 
|  | 158         else | 
|  | 159         { | 
|  | 160           // This array is too large, it needs an individual buffer | 
|  | 161           bufferIndex = addBuffer(referenceLength * newSize, buffers, viewTypes,
      views); | 
|  | 162         } | 
|  | 163 | 
|  | 164         if (size > 0) | 
|  | 165         { | 
|  | 166           let copyBytes = length * referenceLength; | 
|  | 167           let src = Uint8Array(buffers[this.arrayBufferIndex], this.arrayByteOff
     set, copyBytes); | 
|  | 168           let dst = Uint8Array(buffers[bufferIndex], byteOffset, copyBytes); | 
|  | 169           dst.set(src); | 
|  | 170         } | 
|  | 171 | 
|  | 172         this.arrayBufferIndex = bufferIndex; | 
|  | 173         this.arrayByteOffset = byteOffset; | 
|  | 174       } | 
|  | 175       else | 
|  | 176         this.arrayBufferIndex = -1; | 
|  | 177 | 
|  | 178       if (size > 0) | 
|  | 179       { | 
|  | 180         // Release old buffer | 
|  | 181         let reference = firstFree[size]; | 
|  | 182         if (typeof reference != "undefined") | 
|  | 183           dealloc(reference, origBufferIndex, origByteOffset); | 
|  | 184         else | 
|  | 185           removeBuffer(origBufferIndex, buffers, views); | 
|  | 186       } | 
|  | 187     } | 
|  | 188 | 
|  | 189     return newSize; | 
|  | 190   } | 
|  | 191 } | 
|  | 192 | 
|  | 193 function createArrayType(elementType, typeDescriptor, meta) | 
|  | 194 { | 
|  | 195   if (typeof meta != "object" || meta == null) | 
|  | 196     meta = {}; | 
|  | 197 | 
|  | 198   // We need to make sure that all buffer chunks are big enough to hold a | 
|  | 199   // reference in order to manage the free chunks as a linked list. Each array | 
|  | 200   // buffer should be dedicated to arrays of particular size - the number of | 
|  | 201   // possible sizes is limited as the sizes can only be powers of two. | 
|  | 202   let {TypedReference} = require("typedObjects/references"); | 
|  | 203   let minElements = nextPow2(Math.max(Math.ceil(TypedReference.byteLength / elem
     entType.referenceLength) | 0, 1)); | 
|  | 204   let bufferSize = ("arrayBufferSize" in meta ? meta.arrayBufferSize | 0 : 1024)
     ; | 
|  | 205   bufferSize = nextPow2(Math.max(bufferSize, minElements * 2)) | 0; | 
|  | 206 | 
|  | 207   let buffers = []; | 
|  | 208   let viewTypes = elementType.viewTypes; | 
|  | 209   let views = []; | 
|  | 210   for (let i = 0, l = viewTypes.length | 0; i < l; i++) | 
|  | 211     views.push([]); | 
|  | 212 | 
|  | 213   let elementGetter = elementType.createGetter.apply(elementType, [0].concat(vie
     ws)); | 
|  | 214   let elementSetter = elementType.createSetter.apply(elementType, [0].concat(vie
     ws)); | 
|  | 215 | 
|  | 216   let typedReferenceTypes = TypedReference.viewTypes; | 
|  | 217   let typedReferenceViews = []; | 
|  | 218   for (let i = 0, l = typedReferenceTypes.length | 0; i < l; i++) | 
|  | 219   { | 
|  | 220     let type = typedReferenceTypes[i]; | 
|  | 221     let index = viewTypes.indexOf(type); | 
|  | 222     if (index < 0) | 
|  | 223     { | 
|  | 224       viewTypes.push(type); | 
|  | 225       views.push([]); | 
|  | 226       index = viewTypes.length - 1; | 
|  | 227     } | 
|  | 228     typedReferenceViews.push(views[index]); | 
|  | 229   } | 
|  | 230 | 
|  | 231   let firstFree = []; | 
|  | 232   for (let i = minElements; i < bufferSize; i <<= 1) | 
|  | 233     firstFree[i] = new TypedReference(-1, typedReferenceViews); | 
|  | 234 | 
|  | 235   let {int16, uint32} = require("typedObjects/primitiveTypes"); | 
|  | 236   typeDescriptor = Object.create(typeDescriptor || {}); | 
|  | 237   typeDescriptor.arrayBufferIndex = int16; | 
|  | 238   typeDescriptor.arrayByteOffset = uint32; | 
|  | 239   typeDescriptor.length = uint32; | 
|  | 240   typeDescriptor.size = uint32; | 
|  | 241 | 
|  | 242   let elementShift = ilog2(elementType.referenceLength | 0); | 
|  | 243   typeDescriptor.get = createGetter(elementGetter, elementShift); | 
|  | 244   typeDescriptor.set = createSetter(elementSetter, elementShift); | 
|  | 245 | 
|  | 246   if (meta.hasOwnProperty("constructor") && typeof meta.constructor == "function
     ") | 
|  | 247     meta.constructor = createCombinedConstructor(meta.constructor); | 
|  | 248   else | 
|  | 249     meta.constructor = defaultArrayConstructor; | 
|  | 250 | 
|  | 251   if (meta.hasOwnProperty("destructor") && typeof meta.destructor == "function") | 
|  | 252     meta.destructor = createCombinedDestructor(meta.destructor); | 
|  | 253   else | 
|  | 254     meta.destructor = defaultArrayDestructor; | 
|  | 255 | 
|  | 256   if (!meta.watch || typeof meta.watch != "object") | 
|  | 257     meta.watch = {}; | 
|  | 258 | 
|  | 259   meta.watch.length = createLengthWatcher(elementType, elementSetter); | 
|  | 260   meta.watch.size = createSizeWatcher(elementType, minElements, bufferSize, buff
     ers, viewTypes, views, firstFree); | 
|  | 261 | 
|  | 262   let {ObjectBase} = require("typedObjects/objectTypes"); | 
|  | 263   return ObjectBase.extend(typeDescriptor, meta); | 
|  | 264 } | 
|  | 265 | 
|  | 266 exports.createArrayType = createArrayType; | 
| OLD | NEW | 
|---|