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 |