Chromium Code Reviews| 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 'b_plus_tree.dart'; | |
| 11 | |
| 12 | |
| 13 /** | |
| 14 * A [Codec] encodes and decodes data. | |
| 15 */ | |
| 16 abstract class Codec<E> { | |
| 17 /** | |
| 18 * The size of the value in bytes. | |
| 19 */ | |
| 20 int get sizeInBytes; | |
| 21 | |
| 22 /** | |
| 23 * Returns the value decoded from [buffer]. | |
| 24 * | |
| 25 * The given [buffer] has exactly [sizeInBytes] bytes length. | |
| 26 */ | |
| 27 E decode(ByteData buffer); | |
| 28 | |
| 29 /** | |
| 30 * Encodes [value] into [buffer]. | |
| 31 * | |
| 32 * The given [buffer] has exactly [sizeInBytes] bytes length. | |
| 33 */ | |
| 34 void encode(ByteData buffer, E value); | |
| 35 } | |
| 36 | |
| 37 | |
| 38 /** | |
| 39 * A [Codec] for strings with a predefined maximum length. | |
| 40 */ | |
| 41 class FixedStringCodec implements Codec<String> { | |
| 42 final int maxLength; | |
| 43 final int sizeInBytes; | |
| 44 | |
| 45 const FixedStringCodec(int maxLength) | |
| 46 : maxLength = maxLength, | |
| 47 sizeInBytes = 2 + 2 * maxLength; | |
| 48 | |
| 49 @override | |
| 50 String decode(ByteData buffer) { | |
| 51 int length = buffer.getUint16(0); | |
| 52 int offset = 2; | |
| 53 List<int> codeUnits = new List<int>(length); | |
| 54 for (int i = 0; i < length; i++) { | |
| 55 codeUnits[i] = buffer.getUint16(offset); | |
| 56 offset += 2; | |
| 57 } | |
| 58 return new String.fromCharCodes(codeUnits); | |
| 59 } | |
| 60 | |
| 61 @override | |
| 62 void encode(ByteData buffer, String value) { | |
| 63 int length = value.length; | |
| 64 if (length > maxLength) { | |
| 65 throw new ArgumentError( | |
| 66 'String $value length=$length is greater than allowed $maxLength'); | |
| 67 } | |
| 68 buffer.setUint16(0, length); | |
| 69 int offset = 2; | |
| 70 for (int codeUnit in value.codeUnits) { | |
| 71 buffer.setUint16(offset, codeUnit); | |
| 72 offset += 2; | |
| 73 } | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 | |
| 78 /** | |
| 79 * A [PageManager] that keeps all [Uint8List] pages in memory. | |
| 80 */ | |
| 81 class MemoryPageManager implements PageManager { | |
| 82 final int pageSizeInBytes; | |
| 83 int _nextPage = 0; | |
| 84 final Map<int, Uint8List> _pages = new HashMap<int, Uint8List>(); | |
|
Paul Berry
2014/06/10 18:39:08
Kind of surprised that you're using a Map here, si
scheglov
2014/06/10 20:12:12
I don't observe any performance difference with on
| |
| 85 | |
| 86 MemoryPageManager(this.pageSizeInBytes); | |
| 87 | |
| 88 @override | |
| 89 int alloc() { | |
| 90 int id = _nextPage++; | |
| 91 Uint8List page = new Uint8List(pageSizeInBytes); | |
| 92 _pages[id] = page; | |
| 93 return id; | |
| 94 } | |
| 95 | |
| 96 @override | |
| 97 void free(int id) { | |
| 98 Uint8List page = _pages.remove(id); | |
| 99 if (page == null) { | |
| 100 throw new StateError('Page $id has been already freed.'); | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 @override | |
| 105 Uint8List read(int id) { | |
| 106 Uint8List page = _pages[id]; | |
| 107 if (page == null) { | |
| 108 throw new StateError('Page $id does not exist.'); | |
| 109 } | |
| 110 return page; | |
| 111 } | |
| 112 | |
| 113 @override | |
| 114 void write(int id, Uint8List page) { | |
| 115 if (!_pages.containsKey(id)) { | |
|
Paul Berry
2014/06/10 18:39:08
Might want to add a check here to verify that page
scheglov
2014/06/10 20:12:12
Done.
| |
| 116 throw new StateError('Page $id does not exist.'); | |
| 117 } | |
| 118 _pages[id] = page; | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 | |
| 123 /** | |
| 124 * [PageManager] allows to allocate, read, write and free [Uint8List] pages. | |
| 125 */ | |
| 126 abstract class PageManager { | |
| 127 /** | |
| 128 * The size of pages provided by this [PageManager]. | |
| 129 */ | |
| 130 int get pageSizeInBytes; | |
| 131 | |
| 132 /** | |
| 133 * Allocates a new page and returns its identifier. | |
| 134 */ | |
| 135 int alloc(); | |
| 136 | |
| 137 /** | |
| 138 * Frees the page with the given identifier. | |
| 139 */ | |
| 140 void free(int id); | |
| 141 | |
| 142 /** | |
| 143 * Reads the page with the given identifier and returns its content. | |
|
Paul Berry
2014/06/10 18:39:08
Is the caller allowed to modify the contents of th
scheglov
2014/06/10 20:12:12
Done.
| |
| 144 */ | |
| 145 Uint8List read(int id); | |
| 146 | |
| 147 /** | |
| 148 * Writes the given page. | |
| 149 */ | |
| 150 void write(int id, Uint8List page); | |
| 151 } | |
| 152 | |
| 153 | |
| 154 /** | |
| 155 * A [NodeManager] that keeps nodes in [PageManager]. | |
| 156 */ | |
| 157 class PageNodeManager<K, V> implements NodeManager<K, V, int> { | |
|
Paul Berry
2014/06/10 18:39:08
To make it easier to understand (and use) this cla
scheglov
2014/06/10 20:12:12
Yes, already done in one of the subsequent CLs.
I'
| |
| 158 static const int INDEX_OFFSET_DATA = 4; | |
| 159 static const int INDEX_OFFSET_KEY_COUNT = 0; | |
| 160 static const int LEAF_OFFSET_DATA = 4; | |
| 161 static const int LEAF_OFFSET_KEY_COUNT = 0; | |
| 162 | |
| 163 final Set<int> indexPages = new HashSet<int>(); | |
| 164 Codec<K> keyCodec; | |
| 165 final Set<int> leafPages = new HashSet<int>(); | |
| 166 PageManager pageManager; | |
| 167 Codec<V> valueCodec; | |
| 168 | |
| 169 PageNodeManager(this.pageManager, this.keyCodec, this.valueCodec); | |
| 170 | |
| 171 @override | |
| 172 int createIndex() { | |
| 173 int id = pageManager.alloc(); | |
| 174 indexPages.add(id); | |
| 175 return id; | |
| 176 } | |
| 177 | |
| 178 @override | |
| 179 int createLeaf() { | |
| 180 int id = pageManager.alloc(); | |
| 181 leafPages.add(id); | |
| 182 return id; | |
| 183 } | |
| 184 | |
| 185 @override | |
| 186 void delete(int id) { | |
| 187 pageManager.free(id); | |
| 188 indexPages.remove(id); | |
| 189 leafPages.remove(id); | |
| 190 } | |
| 191 | |
| 192 @override | |
| 193 bool isIndex(int id) { | |
| 194 return indexPages.contains(id); | |
| 195 } | |
| 196 | |
| 197 @override | |
| 198 IndexNodeData<K, int> readIndex(int id) { | |
| 199 Uint8List page = pageManager.read(id); | |
| 200 // read header | |
| 201 int keyCount; | |
| 202 { | |
| 203 ByteData data = new ByteData.view(page.buffer); | |
| 204 keyCount = data.getInt32(INDEX_OFFSET_KEY_COUNT); | |
| 205 } | |
| 206 // read keys/children | |
| 207 List<K> keys = new List<K>(); | |
| 208 List<int> children = new List<int>(); | |
| 209 int keySize = keyCodec.sizeInBytes; | |
| 210 int offset = INDEX_OFFSET_DATA; | |
| 211 for (int i = 0; i < keyCount; i++) { | |
| 212 // read child | |
| 213 { | |
| 214 ByteData byteData = new ByteData.view(page.buffer, offset); | |
|
Paul Berry
2014/06/10 18:39:08
I'm a little worried that creating all of these vi
| |
| 215 int childPage = byteData.getUint32(0); | |
| 216 children.add(childPage); | |
| 217 offset += 4; | |
| 218 } | |
| 219 // read key | |
| 220 { | |
| 221 ByteData byteData = new ByteData.view(page.buffer, offset, keySize); | |
| 222 K key = keyCodec.decode(byteData); | |
| 223 keys.add(key); | |
| 224 offset += keySize; | |
| 225 } | |
| 226 } | |
| 227 // read last child | |
| 228 { | |
| 229 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 230 int childPage = byteData.getUint32(0); | |
| 231 children.add(childPage); | |
| 232 } | |
| 233 // done | |
| 234 return new IndexNodeData<K, int>(keys, children); | |
| 235 } | |
| 236 | |
| 237 @override | |
| 238 LeafNodeData<K, V> readLeaf(int id) { | |
| 239 Uint8List page = pageManager.read(id); | |
| 240 // read header | |
| 241 int keyCount; | |
| 242 { | |
| 243 ByteData data = new ByteData.view(page.buffer); | |
| 244 keyCount = data.getInt32(LEAF_OFFSET_KEY_COUNT); | |
| 245 } | |
| 246 // read keys/children | |
| 247 List<K> keys = new List<K>(); | |
| 248 List<V> values = new List<V>(); | |
| 249 int keySize = keyCodec.sizeInBytes; | |
| 250 int valueSize = valueCodec.sizeInBytes; | |
| 251 int offset = LEAF_OFFSET_DATA; | |
| 252 for (int i = 0; i < keyCount; i++) { | |
| 253 // read key | |
| 254 { | |
| 255 ByteData byteData = new ByteData.view(page.buffer, offset, keySize); | |
| 256 K key = keyCodec.decode(byteData); | |
| 257 keys.add(key); | |
| 258 offset += keySize; | |
| 259 } | |
| 260 // read value | |
| 261 { | |
| 262 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 263 V value = valueCodec.decode(byteData); | |
| 264 values.add(value); | |
| 265 offset += valueSize; | |
| 266 } | |
| 267 } | |
| 268 // done | |
| 269 return new LeafNodeData<K, V>(keys, values); | |
| 270 } | |
| 271 | |
| 272 @override | |
| 273 void writeIndex(int id, IndexNodeData<K, int> data) { | |
| 274 Uint8List page = new Uint8List(pageManager.pageSizeInBytes); | |
| 275 // write header | |
| 276 int keyCount = data.keys.length; | |
| 277 { | |
| 278 ByteData byteData = new ByteData.view(page.buffer); | |
| 279 byteData.setUint32(INDEX_OFFSET_KEY_COUNT, keyCount); | |
| 280 } | |
| 281 // write keys/children | |
| 282 int keySize = keyCodec.sizeInBytes; | |
| 283 int offset = INDEX_OFFSET_DATA; | |
| 284 for (int i = 0; i < keyCount; i++) { | |
| 285 // write child | |
| 286 { | |
| 287 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 288 byteData.setUint32(0, data.children[i]); | |
| 289 offset += 4; | |
| 290 } | |
| 291 // write key | |
| 292 { | |
| 293 ByteData byteData = new ByteData.view(page.buffer, offset, keySize); | |
| 294 keyCodec.encode(byteData, data.keys[i]); | |
| 295 offset += keySize; | |
| 296 } | |
| 297 } | |
| 298 // write last child | |
| 299 { | |
| 300 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 301 byteData.setUint32(0, data.children.last); | |
| 302 } | |
| 303 // write page | |
| 304 pageManager.write(id, page); | |
| 305 } | |
| 306 | |
| 307 @override | |
| 308 void writeLeaf(int id, LeafNodeData<K, V> data) { | |
| 309 Uint8List page = new Uint8List(pageManager.pageSizeInBytes); | |
| 310 // write header | |
| 311 int keyCount = data.keys.length; | |
| 312 { | |
| 313 ByteData byteData = new ByteData.view(page.buffer); | |
| 314 byteData.setUint32(LEAF_OFFSET_KEY_COUNT, keyCount); | |
| 315 } | |
| 316 // write keys/values | |
| 317 int keySize = keyCodec.sizeInBytes; | |
| 318 int valueSize = valueCodec.sizeInBytes; | |
| 319 int offset = LEAF_OFFSET_DATA; | |
| 320 for (int i = 0; i < keyCount; i++) { | |
| 321 // write key | |
| 322 { | |
| 323 ByteData byteData = new ByteData.view(page.buffer, offset, keySize); | |
| 324 keyCodec.encode(byteData, data.keys[i]); | |
| 325 offset += keySize; | |
| 326 } | |
| 327 // write value | |
| 328 { | |
| 329 ByteData byteData = new ByteData.view(page.buffer, offset); | |
| 330 valueCodec.encode(byteData, data.values[i]); | |
| 331 offset += valueSize; | |
| 332 } | |
| 333 } | |
| 334 // write page | |
| 335 pageManager.write(id, page); | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 | |
| 340 /** | |
| 341 * A [Codec] for unsigned 32-bit integers. | |
| 342 */ | |
| 343 class Uint32Codec implements Codec<int> { | |
| 344 static const Uint32Codec INSTANCE = const Uint32Codec._(); | |
| 345 | |
| 346 const Uint32Codec._(); | |
| 347 | |
| 348 @override | |
| 349 int get sizeInBytes => 4; | |
| 350 | |
| 351 @override | |
| 352 int decode(ByteData buffer) { | |
| 353 return buffer.getUint32(0); | |
| 354 } | |
| 355 | |
| 356 @override | |
| 357 void encode(ByteData buffer, int element) { | |
| 358 buffer.setUint32(0, element); | |
| 359 } | |
| 360 } | |
| OLD | NEW |