| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library index.page_node_manager; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 import 'dart:typed_data'; | |
| 9 | |
| 10 import 'package:analysis_server/src/index/b_plus_tree.dart'; | |
| 11 import 'package:analysis_server/src/index/lru_cache.dart'; | |
| 12 | |
| 13 | |
| 14 /** | |
| 15 * A [NodeManager] that caches a specified number of index and leaf nodes. | |
| 16 */ | |
| 17 class CachingNodeManager<K, V, N> implements NodeManager<K, V, N> { | |
| 18 final NodeManager<K, V, N> _delegate; | |
| 19 LRUCache<N, IndexNodeData<K, N>> _indexCache; | |
| 20 LRUCache<N, LeafNodeData<K, V>> _leafCache; | |
| 21 | |
| 22 CachingNodeManager(this._delegate, int indexNodeCacheSize, | |
| 23 int leafNodeCacheSize) { | |
| 24 _indexCache = new LRUCache<N, IndexNodeData<K, N>>(indexNodeCacheSize, | |
| 25 _delegate.writeIndex); | |
| 26 _leafCache = new LRUCache<N, LeafNodeData<K, V>>(leafNodeCacheSize, | |
| 27 _delegate.writeLeaf); | |
| 28 } | |
| 29 | |
| 30 @override | |
| 31 int get maxIndexKeys => _delegate.maxIndexKeys; | |
| 32 | |
| 33 @override | |
| 34 int get maxLeafKeys => _delegate.maxLeafKeys; | |
| 35 | |
| 36 @override | |
| 37 N createIndex() { | |
| 38 return _delegate.createIndex(); | |
| 39 } | |
| 40 | |
| 41 @override | |
| 42 N createLeaf() { | |
| 43 return _delegate.createLeaf(); | |
| 44 } | |
| 45 | |
| 46 @override | |
| 47 void delete(N id) { | |
| 48 _indexCache.remove(id); | |
| 49 _leafCache.remove(id); | |
| 50 _delegate.delete(id); | |
| 51 } | |
| 52 | |
| 53 @override | |
| 54 bool isIndex(N id) { | |
| 55 return _delegate.isIndex(id); | |
| 56 } | |
| 57 | |
| 58 @override | |
| 59 IndexNodeData<K, N> readIndex(N id) { | |
| 60 IndexNodeData<K, N> data = _indexCache.get(id); | |
| 61 if (data == null) { | |
| 62 data = _delegate.readIndex(id); | |
| 63 _indexCache.put(id, data); | |
| 64 } | |
| 65 return data; | |
| 66 } | |
| 67 | |
| 68 @override | |
| 69 LeafNodeData<K, V> readLeaf(N id) { | |
| 70 LeafNodeData<K, V> data = _leafCache.get(id); | |
| 71 if (data == null) { | |
| 72 data = _delegate.readLeaf(id); | |
| 73 _leafCache.put(id, data); | |
| 74 } | |
| 75 return data; | |
| 76 } | |
| 77 | |
| 78 @override | |
| 79 void writeIndex(N id, IndexNodeData<K, N> data) { | |
| 80 _indexCache.put(id, data); | |
| 81 } | |
| 82 | |
| 83 @override | |
| 84 void writeLeaf(N id, LeafNodeData<K, V> data) { | |
| 85 _leafCache.put(id, data); | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 | |
| 90 /** | |
| 91 * A [Codec] encodes and decodes data. | |
| 92 */ | |
| 93 abstract class Codec<E> { | |
| 94 /** | |
| 95 * The size of the value in bytes. | |
| 96 */ | |
| 97 int get sizeInBytes; | |
| 98 | |
| 99 /** | |
| 100 * Returns the value decoded from [buffer]. | |
| 101 * | |
| 102 * The given [buffer] has exactly [sizeInBytes] bytes length. | |
| 103 */ | |
| 104 E decode(ByteData buffer); | |
| 105 | |
| 106 /** | |
| 107 * Encodes [value] into [buffer]. | |
| 108 * | |
| 109 * The given [buffer] has exactly [sizeInBytes] bytes length. | |
| 110 */ | |
| 111 void encode(ByteData buffer, E value); | |
| 112 } | |
| 113 | |
| 114 | |
| 115 /** | |
| 116 * A [Codec] for strings with a predefined maximum length. | |
| 117 */ | |
| 118 class FixedStringCodec implements Codec<String> { | |
| 119 final int maxLength; | |
| 120 final int sizeInBytes; | |
| 121 | |
| 122 const FixedStringCodec(int maxLength) | |
| 123 : maxLength = maxLength, | |
| 124 sizeInBytes = 2 + 2 * maxLength; | |
| 125 | |
| 126 @override | |
| 127 String decode(ByteData buffer) { | |
| 128 int length = buffer.getUint16(0); | |
| 129 int offset = 2; | |
| 130 List<int> codeUnits = new List<int>(length); | |
| 131 for (int i = 0; i < length; i++) { | |
| 132 codeUnits[i] = buffer.getUint16(offset); | |
| 133 offset += 2; | |
| 134 } | |
| 135 return new String.fromCharCodes(codeUnits); | |
| 136 } | |
| 137 | |
| 138 @override | |
| 139 void encode(ByteData buffer, String value) { | |
| 140 int length = value.length; | |
| 141 if (length > maxLength) { | |
| 142 throw new ArgumentError( | |
| 143 'String $value length=$length is greater than allowed $maxLength'); | |
| 144 } | |
| 145 buffer.setUint16(0, length); | |
| 146 int offset = 2; | |
| 147 List<int> codeUnits = value.codeUnits; | |
| 148 for (int i = 0; i < length; i++) { | |
| 149 buffer.setUint16(offset, codeUnits[i]); | |
| 150 offset += 2; | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 | |
| 156 /** | |
| 157 * A [PageManager] that keeps all [Uint8List] pages in memory. | |
| 158 */ | |
| 159 class MemoryPageManager implements PageManager { | |
| 160 final int pageSizeInBytes; | |
| 161 int _nextPage = 0; | |
| 162 final Map<int, Uint8List> _pages = new HashMap<int, Uint8List>(); | |
| 163 | |
| 164 MemoryPageManager(this.pageSizeInBytes); | |
| 165 | |
| 166 @override | |
| 167 int alloc() { | |
| 168 int id = _nextPage++; | |
| 169 Uint8List page = new Uint8List(pageSizeInBytes); | |
| 170 _pages[id] = page; | |
| 171 return id; | |
| 172 } | |
| 173 | |
| 174 @override | |
| 175 void free(int id) { | |
| 176 Uint8List page = _pages.remove(id); | |
| 177 if (page == null) { | |
| 178 throw new StateError('Page $id has been already freed.'); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 @override | |
| 183 Uint8List read(int id) { | |
| 184 Uint8List page = _pages[id]; | |
| 185 if (page == null) { | |
| 186 throw new StateError('Page $id does not exist.'); | |
| 187 } | |
| 188 return page; | |
| 189 } | |
| 190 | |
| 191 @override | |
| 192 void write(int id, Uint8List page) { | |
| 193 if (!_pages.containsKey(id)) { | |
| 194 throw new StateError('Page $id does not exist.'); | |
| 195 } | |
| 196 if (page.length != pageSizeInBytes) { | |
| 197 throw new ArgumentError('Page $id has length ${page.length}, ' | |
| 198 'but $pageSizeInBytes is expected.'); | |
| 199 } | |
| 200 _pages[id] = page; | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 | |
| 205 /** | |
| 206 * [PageManager] allows to allocate, read, write and free [Uint8List] pages. | |
| 207 */ | |
| 208 abstract class PageManager { | |
| 209 /** | |
| 210 * The size of pages provided by this [PageManager]. | |
| 211 */ | |
| 212 int get pageSizeInBytes; | |
| 213 | |
| 214 /** | |
| 215 * Allocates a new page and returns its identifier. | |
| 216 */ | |
| 217 int alloc(); | |
| 218 | |
| 219 /** | |
| 220 * Frees the page with the given identifier. | |
| 221 */ | |
| 222 void free(int id); | |
| 223 | |
| 224 /** | |
| 225 * Reads the page with the given identifier and returns its content. | |
| 226 * | |
| 227 * An internal representation of the page is returned, any changes made to it | |
| 228 * may be accessible to other clients reading the same page. | |
| 229 */ | |
| 230 Uint8List read(int id); | |
| 231 | |
| 232 /** | |
| 233 * Writes the given page. | |
| 234 */ | |
| 235 void write(int id, Uint8List page); | |
| 236 } | |
| 237 | |
| 238 | |
| 239 /** | |
| 240 * A [NodeManager] that keeps nodes in [PageManager]. | |
| 241 */ | |
| 242 class PageNodeManager<K, V> implements NodeManager<K, V, int> { | |
| 243 static const int _INDEX_OFFSET_DATA = 4; | |
| 244 static const int _INDEX_OFFSET_KEY_COUNT = 0; | |
| 245 static const int _LEAF_OFFSET_DATA = 4; | |
| 246 static const int _LEAF_OFFSET_KEY_COUNT = 0; | |
| 247 | |
| 248 final Set<int> _indexPages = new HashSet<int>(); | |
| 249 Codec<K> _keyCodec; | |
| 250 final Set<int> _leafPages = new HashSet<int>(); | |
| 251 PageManager _pageManager; | |
| 252 Codec<V> _valueCodec; | |
| 253 | |
| 254 PageNodeManager(this._pageManager, this._keyCodec, this._valueCodec); | |
| 255 | |
| 256 @override | |
| 257 int get maxIndexKeys { | |
| 258 int keySize = _keyCodec.sizeInBytes; | |
| 259 int childSize = 4; | |
| 260 int dataSize = _pageManager.pageSizeInBytes - _INDEX_OFFSET_DATA; | |
| 261 return (dataSize - childSize) ~/ (keySize + childSize); | |
| 262 } | |
| 263 | |
| 264 @override | |
| 265 int get maxLeafKeys { | |
| 266 int keySize = _keyCodec.sizeInBytes; | |
| 267 int valueSize = _valueCodec.sizeInBytes; | |
| 268 int dataSize = _pageManager.pageSizeInBytes - _INDEX_OFFSET_DATA; | |
| 269 return dataSize ~/ (keySize + valueSize); | |
| 270 } | |
| 271 | |
| 272 @override | |
| 273 int createIndex() { | |
| 274 int id = _pageManager.alloc(); | |
| 275 _indexPages.add(id); | |
| 276 return id; | |
| 277 } | |
| 278 | |
| 279 @override | |
| 280 int createLeaf() { | |
| 281 int id = _pageManager.alloc(); | |
| 282 _leafPages.add(id); | |
| 283 return id; | |
| 284 } | |
| 285 | |
| 286 @override | |
| 287 void delete(int id) { | |
| 288 _pageManager.free(id); | |
| 289 _indexPages.remove(id); | |
| 290 _leafPages.remove(id); | |
| 291 } | |
| 292 | |
| 293 @override | |
| 294 bool isIndex(int id) { | |
| 295 return _indexPages.contains(id); | |
| 296 } | |
| 297 | |
| 298 @override | |
| 299 IndexNodeData<K, int> readIndex(int id) { | |
| 300 Uint8List page = _pageManager.read(id); | |
| 301 // read header | |
| 302 int keyCount; | |
| 303 { | |
| 304 ByteData data = new ByteData.view(page.buffer); | |
| 305 keyCount = data.getInt32(_INDEX_OFFSET_KEY_COUNT); | |
| 306 } | |
| 307 // read keys/children | |
| 308 List<K> keys = new List<K>(); | |
| 309 List<int> children = new List<int>(); | |
| 310 int keySize = _keyCodec.sizeInBytes; | |
| 311 int offset = _INDEX_OFFSET_DATA; | |
| 312 for (int i = 0; i < keyCount; i++) { | |
| 313 // read child | |
| 314 { | |
| 315 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 316 int childPage = byteData.getUint32(0); | |
| 317 children.add(childPage); | |
| 318 offset += 4; | |
| 319 } | |
| 320 // read key | |
| 321 { | |
| 322 ByteData byteData = new ByteData.view(page.buffer, offset, keySize); | |
| 323 K key = _keyCodec.decode(byteData); | |
| 324 keys.add(key); | |
| 325 offset += keySize; | |
| 326 } | |
| 327 } | |
| 328 // read last child | |
| 329 { | |
| 330 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 331 int childPage = byteData.getUint32(0); | |
| 332 children.add(childPage); | |
| 333 } | |
| 334 // done | |
| 335 return new IndexNodeData<K, int>(keys, children); | |
| 336 } | |
| 337 | |
| 338 @override | |
| 339 LeafNodeData<K, V> readLeaf(int id) { | |
| 340 Uint8List page = _pageManager.read(id); | |
| 341 // read header | |
| 342 int keyCount; | |
| 343 { | |
| 344 ByteData data = new ByteData.view(page.buffer); | |
| 345 keyCount = data.getInt32(_LEAF_OFFSET_KEY_COUNT); | |
| 346 } | |
| 347 // read keys/children | |
| 348 List<K> keys = new List<K>(); | |
| 349 List<V> values = new List<V>(); | |
| 350 int keySize = _keyCodec.sizeInBytes; | |
| 351 int valueSize = _valueCodec.sizeInBytes; | |
| 352 int offset = _LEAF_OFFSET_DATA; | |
| 353 for (int i = 0; i < keyCount; i++) { | |
| 354 // read key | |
| 355 { | |
| 356 ByteData byteData = new ByteData.view(page.buffer, offset, keySize); | |
| 357 K key = _keyCodec.decode(byteData); | |
| 358 keys.add(key); | |
| 359 offset += keySize; | |
| 360 } | |
| 361 // read value | |
| 362 { | |
| 363 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 364 V value = _valueCodec.decode(byteData); | |
| 365 values.add(value); | |
| 366 offset += valueSize; | |
| 367 } | |
| 368 } | |
| 369 // done | |
| 370 return new LeafNodeData<K, V>(keys, values); | |
| 371 } | |
| 372 | |
| 373 @override | |
| 374 void writeIndex(int id, IndexNodeData<K, int> data) { | |
| 375 Uint8List page = new Uint8List(_pageManager.pageSizeInBytes); | |
| 376 // write header | |
| 377 int keyCount = data.keys.length; | |
| 378 { | |
| 379 ByteData byteData = new ByteData.view(page.buffer); | |
| 380 byteData.setUint32(PageNodeManager._INDEX_OFFSET_KEY_COUNT, keyCount); | |
| 381 } | |
| 382 // write keys/children | |
| 383 int keySize = _keyCodec.sizeInBytes; | |
| 384 int offset = PageNodeManager._INDEX_OFFSET_DATA; | |
| 385 for (int i = 0; i < keyCount; i++) { | |
| 386 // write child | |
| 387 { | |
| 388 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 389 byteData.setUint32(0, data.children[i]); | |
| 390 offset += 4; | |
| 391 } | |
| 392 // write key | |
| 393 { | |
| 394 ByteData byteData = new ByteData.view(page.buffer, offset, keySize); | |
| 395 _keyCodec.encode(byteData, data.keys[i]); | |
| 396 offset += keySize; | |
| 397 } | |
| 398 } | |
| 399 // write last child | |
| 400 { | |
| 401 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 402 byteData.setUint32(0, data.children.last); | |
| 403 } | |
| 404 // write page | |
| 405 _pageManager.write(id, page); | |
| 406 } | |
| 407 | |
| 408 @override | |
| 409 void writeLeaf(int id, LeafNodeData<K, V> data) { | |
| 410 Uint8List page = new Uint8List(_pageManager.pageSizeInBytes); | |
| 411 // write header | |
| 412 int keyCount = data.keys.length; | |
| 413 { | |
| 414 ByteData byteData = new ByteData.view(page.buffer); | |
| 415 byteData.setUint32(PageNodeManager._LEAF_OFFSET_KEY_COUNT, keyCount); | |
| 416 } | |
| 417 // write keys/values | |
| 418 int keySize = _keyCodec.sizeInBytes; | |
| 419 int valueSize = _valueCodec.sizeInBytes; | |
| 420 int offset = PageNodeManager._LEAF_OFFSET_DATA; | |
| 421 for (int i = 0; i < keyCount; i++) { | |
| 422 // write key | |
| 423 { | |
| 424 ByteData byteData = new ByteData.view(page.buffer, offset, keySize); | |
| 425 _keyCodec.encode(byteData, data.keys[i]); | |
| 426 offset += keySize; | |
| 427 } | |
| 428 // write value | |
| 429 { | |
| 430 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 431 _valueCodec.encode(byteData, data.values[i]); | |
| 432 offset += valueSize; | |
| 433 } | |
| 434 } | |
| 435 // write page | |
| 436 _pageManager.write(id, page); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 | |
| 441 /** | |
| 442 * A [Codec] for unsigned 32-bit integers. | |
| 443 */ | |
| 444 class Uint32Codec implements Codec<int> { | |
| 445 static const Uint32Codec INSTANCE = const Uint32Codec._(); | |
| 446 | |
| 447 const Uint32Codec._(); | |
| 448 | |
| 449 @override | |
| 450 int get sizeInBytes => 4; | |
| 451 | |
| 452 @override | |
| 453 int decode(ByteData buffer) { | |
| 454 return buffer.getUint32(0); | |
| 455 } | |
| 456 | |
| 457 @override | |
| 458 void encode(ByteData buffer, int element) { | |
| 459 buffer.setUint32(0, element); | |
| 460 } | |
| 461 } | |
| OLD | NEW |