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 const LOAD_FACTOR = 0.75; |
| 21 |
| 22 let {calculateHash} = require("typedObjects/hash"); |
| 23 let {ilog2, nextPow2, alloc, dealloc, addBuffer, removeBuffer} = require("typedO
bjects/utils"); |
| 24 |
| 25 function defaultDictionaryConstructor() |
| 26 { |
| 27 this.dictionaryBufferIndex = -1; |
| 28 }; |
| 29 |
| 30 function defaultDictionaryDestructor() |
| 31 { |
| 32 this.buckets = 0; |
| 33 }; |
| 34 |
| 35 function lookup(key, groups, view, offset) |
| 36 { |
| 37 let bucketCount = groups << 5; |
| 38 let bucket = calculateHash(key) & (bucketCount - 1); |
| 39 let group = bucket >>> 5; |
| 40 let bit = 1 << (bucket & 0x1F); |
| 41 |
| 42 } |
| 43 |
| 44 function createCombinedConstructor(customConstructor) |
| 45 { |
| 46 return function() |
| 47 { |
| 48 defaultDictionaryConstructor.apply(this); |
| 49 customConstructor.apply(this, arguments); |
| 50 } |
| 51 } |
| 52 |
| 53 function createCombinedDestructor(customDestructor) |
| 54 { |
| 55 return function() |
| 56 { |
| 57 try |
| 58 { |
| 59 customDestructor.apply(this); |
| 60 } |
| 61 finally |
| 62 { |
| 63 defaultDictionaryDestructor.apply(this); |
| 64 } |
| 65 } |
| 66 } |
| 67 |
| 68 function capacityWatcher(newCapacity) |
| 69 { |
| 70 newCapacity = newCapacity | 0; |
| 71 let newBuckets = Math.ceil(newCapacity / LOAD_FACTOR) | 0; |
| 72 if (newBuckets > this.buckets) |
| 73 this.buckets = newBuckets; |
| 74 |
| 75 return (this.buckets * LOAD_FACTOR) | 0; |
| 76 } |
| 77 |
| 78 function createBucketsWatcher(bucketSize, bufferSize, buffers, viewTypes, views,
firstFree) |
| 79 { |
| 80 bufferSize = bufferSize | 0; |
| 81 return function bucketsWatcher(newBuckets) |
| 82 { |
| 83 newBuckets = newBuckets | 0; |
| 84 if (newBuckets < 0) |
| 85 newBuckets = 0; |
| 86 newBuckets = nextPow2(newBuckets); |
| 87 |
| 88 let buckets = this.buckets; |
| 89 if (buckets != newBuckets) |
| 90 { |
| 91 let origBufferIndex = this.dictionaryBufferIndex; |
| 92 let origByteOffset = this.dictionaryByteOffset; |
| 93 if (newBuckets > 0) |
| 94 { |
| 95 // Allocate new buffer |
| 96 let bufferIndex, byteOffset; |
| 97 let reference = firstFree[newGroups]; |
| 98 if (typeof reference != "undefined") |
| 99 { |
| 100 [bufferIndex, byteOffset] = alloc(reference, |
| 101 bucketSize * newBuckets, (bufferSize / newBuckets) | 0, |
| 102 buffers, viewTypes, views); |
| 103 } |
| 104 else |
| 105 { |
| 106 // This dictionary is too large, it needs an individual buffer |
| 107 bufferIndex = addBuffer(bucketSize * newBuckets, buffers, viewTypes, v
iews); |
| 108 byteOffset = 0; |
| 109 } |
| 110 |
| 111 if (buckets > 0) |
| 112 { |
| 113 // TODO: Copy data |
| 114 } |
| 115 |
| 116 this.dictionaryBufferIndex = bufferIndex; |
| 117 this.dictionaryByteOffset = byteOffset; |
| 118 } |
| 119 else |
| 120 this.dictionaryBufferIndex = -1; |
| 121 |
| 122 if (buckets > 0) |
| 123 { |
| 124 // TODO: Release values |
| 125 |
| 126 // Release old buffer |
| 127 let reference = firstFree[buckets]; |
| 128 if (typeof reference != "undefined") |
| 129 dealloc(reference, origBufferIndex, origByteOffset); |
| 130 else |
| 131 removeBuffer(origBufferIndex, buffers, views); |
| 132 } |
| 133 } |
| 134 |
| 135 return newBuckets; |
| 136 } |
| 137 } |
| 138 |
| 139 function createDictionaryType(elementType, typeDescriptor, meta) |
| 140 { |
| 141 if (typeof meta != "object" || meta == null) |
| 142 meta = {}; |
| 143 |
| 144 let {int16, uint32} = require("typedObjects/primitiveTypes"); |
| 145 let {string} = require("typedObjects/stringType"); |
| 146 let {Reference, TypedReference} = require("typedObjects/references"); |
| 147 |
| 148 let groupSize = (elementType.referenceLength + string.referenceLength) * SLOTS
_PER_GROUP + uint32.byteLength * 2; |
| 149 groupSize = Math.max(groupSize, TypedReference.byteLength); |
| 150 let bufferSize = ("dictionaryBufferSize" in meta ? meta.dictionaryBufferSize |
0 : 128); |
| 151 bufferSize = nextPow2(Math.max(bufferSize, 2)) | 0; |
| 152 |
| 153 let buffers = []; |
| 154 let viewTypes = elementType.viewTypes.slice(); |
| 155 let views = []; |
| 156 for (let i = 0, l = viewTypes.length | 0; i < l; i++) |
| 157 views.push([]); |
| 158 |
| 159 let elementGetter = elementType.createGetter.apply(elementType, [0].concat(vie
ws)); |
| 160 let elementSetter = elementType.createSetter.apply(elementType, [0].concat(vie
ws)); |
| 161 |
| 162 let typedReferenceTypes = TypedReference.viewTypes; |
| 163 let typedReferenceViews = []; |
| 164 for (let i = 0, l = typedReferenceTypes.length | 0; i < l; i++) |
| 165 { |
| 166 let type = typedReferenceTypes[i]; |
| 167 let index = viewTypes.indexOf(type); |
| 168 if (index < 0) |
| 169 { |
| 170 viewTypes.push(type); |
| 171 views.push([]); |
| 172 index = viewTypes.length - 1; |
| 173 } |
| 174 typedReferenceViews.push(views[index]); |
| 175 } |
| 176 |
| 177 let firstFree = []; |
| 178 for (let i = 2; i < bufferSize; i <<= 1) |
| 179 firstFree[i] = new TypedReference(-1, typedReferenceViews); |
| 180 |
| 181 // Ensure there is a view for uint32 |
| 182 let uint32Index = viewTypes.indexOf(uint32.viewTypes[0]); |
| 183 if (uint32Index < 0) |
| 184 { |
| 185 viewTypes.push(uint32.viewTypes[0]); |
| 186 views.push([]); |
| 187 uint32Index = viewTypes.length - 1; |
| 188 } |
| 189 |
| 190 let keyGetter = string.createSetter(0) |
| 191 let occupancyGetter = uint32.createGetter( |
| 192 (elementType.referenceLength + string.referenceLength) * SLOTS_PER_GROUP, |
| 193 views[uint32Index] |
| 194 ); |
| 195 let deletedGetter = uint32.createGetter( |
| 196 (elementType.referenceLength + string.referenceLength) * SLOTS_PER_GROUP + u
int32.byteLength, |
| 197 views[uint32Index] |
| 198 ); |
| 199 |
| 200 typeDescriptor = Object.create(typeDescriptor || {}); |
| 201 typeDescriptor.dictionaryBufferIndex = int16; |
| 202 typeDescriptor.dictionaryByteOffset = uint32; |
| 203 typeDescriptor.getDictionaryBuffer = function() |
| 204 { |
| 205 return this.dictionaryBufferIndex >= 0 ? buffers[this.dictionaryBufferIndex]
: null; |
| 206 }; |
| 207 typeDescriptor.groups = uint32; |
| 208 typeDescriptor.capacity = uint32; |
| 209 |
| 210 // typeDescriptor.get = createGetter(elementGetter); |
| 211 // typeDescriptor.set = createSetter(elementSetter); |
| 212 |
| 213 if (meta.hasOwnProperty("constructor") && typeof meta.constructor == "function
") |
| 214 meta.constructor = createCombinedConstructor(meta.constructor); |
| 215 else |
| 216 meta.constructor = defaultDictionaryConstructor; |
| 217 |
| 218 if (meta.hasOwnProperty("destructor") && typeof meta.destructor == "function") |
| 219 meta.destructor = createCombinedDestructor(meta.destructor); |
| 220 else |
| 221 meta.destructor = defaultDictionaryDestructor; |
| 222 |
| 223 if (!meta.watch || typeof meta.watch != "object") |
| 224 meta.watch = {}; |
| 225 |
| 226 meta.watch.groups = createGroupsWatcher(groupSize, bufferSize, buffers, viewTy
pes, views, firstFree); |
| 227 meta.watch.capacity = capacityWatcher; |
| 228 |
| 229 let {ObjectBase} = require("typedObjects/objectTypes"); |
| 230 return ObjectBase.extend(typeDescriptor, meta); |
| 231 } |
| 232 |
| 233 exports.createDictionaryType = createDictionaryType; |
OLD | NEW |