1 /** 2 * D-LevelDB Options 3 * 4 * Database config, read and write options 5 * 6 * Copyright: Copyright © 2013 Byron Heads 7 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 8 * Authors: Byron Heads 9 */ 10 /* Copyright © 2013 Byron Heads 11 * Distributed under the Boost Software License, Version 1.0. 12 * (See accompanying file LICENSE_1_0.txt or copy at 13 * http://www.boost.org/LICENSE_1_0.txt) 14 */ 15 module leveldb.options; 16 17 private import std.algorithm : cmp; 18 private import std..string : toStringz; 19 20 private import leveldb.exceptions; 21 22 private import deimos.leveldb.leveldb; 23 24 /// Create default init read and write options 25 public __gshared const(ReadOptions) DefaultReadOptions; 26 public __gshared const(WriteOptions) DefaultWriteOptions; 27 28 shared static this() 29 { 30 DefaultReadOptions = new ReadOptions; 31 DefaultWriteOptions = new WriteOptions; 32 } 33 34 /** 35 * Database creation and general usgage options 36 */ 37 class Options 38 { 39 private: 40 leveldb_options_t _opt = null; 41 42 /// Store a copy of these objects so they are not cleaned up by the GC 43 Environment _env; 44 Cache _cache; 45 FilterPolicy _filter; 46 Comparator _comparator; 47 48 package: 49 @property 50 inout(leveldb_options_t) ptr() inout 51 { 52 return _opt; 53 } 54 55 public: 56 57 /// Create the internal option object 58 this() 59 { 60 if((_opt = leveldb_options_create()) is null) 61 throw new LeveldbException("Failed to create an option"); 62 } 63 64 /// Destroy any valid option pointer 65 ~this() 66 { 67 if(valid) 68 { 69 leveldb_options_destroy(_opt); 70 _opt = null; 71 } 72 } 73 74 /** If true, the database will be created if it is missing. 75 * Default: false 76 */ 77 @property 78 void create_if_missing(bool val) 79 { 80 leveldb_options_set_create_if_missing(_opt, val); 81 } 82 83 /** If true, an error is raised if the database already exists. 84 * Default: false 85 */ 86 @property 87 void error_if_missing(bool val) 88 { 89 leveldb_options_set_error_if_exists(_opt, val); 90 } 91 92 /** If true, the implementation will do aggressive checking of the 93 * data it is processing and will stop early if it detects any 94 * errors. This may have unforeseen ramifications: for example, a 95 * corruption of one DB entry may cause a large number of entries to 96 * become unreadable or for the entire DB to become unopenable. 97 * Default: false 98 */ 99 @property 100 void paranoid_checks(bool val) 101 { 102 leveldb_options_set_paranoid_checks(_opt, val); 103 } 104 105 /** Compress blocks using the specified compression algorithm. This 106 * parameter can be changed dynamically. 107 * 108 * leveldb_no_compression = 0, 109 * leveldb_snappy_compression = 1 110 * 111 * Default: kSnappyCompression, which gives lightweight but fast 112 * compression. 113 * 114 * Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: 115 * ~200-500MB/s compression 116 * ~400-800MB/s decompression 117 * Note that these speeds are significantly faster than most 118 * persistent storage speeds, and therefore it is typically never 119 * worth switching to kNoCompression. Even if the input data is 120 * incompressible, the kSnappyCompression implementation will 121 * efficiently detect that and will switch to uncompressed mode. 122 */ 123 @property 124 void compression(int val) 125 { 126 leveldb_options_set_compression(_opt, val); 127 } 128 129 /** Parameters that affect performance 130 * Amount of data to build up in memory (backed by an unsorted log 131 * on disk) before converting to a sorted on-disk file. 132 * 133 * Larger values increase performance, especially during bulk loads. 134 * Up to two write buffers may be held in memory at the same time, 135 * so you may wish to adjust this parameter to control memory usage. 136 * Also, a larger write buffer will result in a longer recovery time 137 * the next time the database is opened. 138 * 139 * Default: 4MB 140 */ 141 @property 142 void write_buffer_size(size_t size) 143 { 144 leveldb_options_set_write_buffer_size(_opt, size); 145 } 146 147 /** Number of open files that can be used by the DB. You may need to 148 * increase this if your database has a large working set (budget 149 * one open file per 2MB of working set). 150 * 151 * Default: 1000 152 */ 153 @property 154 void max_open_files(int val) 155 { 156 leveldb_options_set_max_open_files(_opt, val); 157 } 158 159 /** Approximate size of user data packed per block. Note that the 160 * block size specified here corresponds to uncompressed data. The 161 * actual size of the unit read from disk may be smaller if 162 * compression is enabled. This parameter can be changed dynamically. 163 * 164 * Default: 4K 165 */ 166 @property 167 void block_size(size_t size) 168 { 169 leveldb_options_set_block_size(_opt, size); 170 } 171 172 /** Number of keys between restart points for delta encoding of keys. 173 * This parameter can be changed dynamically. Most clients should 174 * leave this parameter alone. 175 * 176 * Default: 16 177 */ 178 @property 179 void block_restart_interval(int val) 180 { 181 leveldb_options_set_block_restart_interval(_opt, val); 182 } 183 184 /** Use the specified object to interact with the environment, 185 * e.g. to read/write files, schedule background work, etc. 186 * Default: Environment() 187 */ 188 @property 189 void env(Environment env) 190 { 191 _env = env; // save pointer so gc doesn't collect it 192 if(env) 193 leveldb_options_set_env(_opt, env._env); 194 else 195 leveldb_options_set_env(_opt, null); 196 } 197 198 /** Control over blocks (user data is stored in a set of blocks, and 199 * a block is the unit of reading from disk). 200 * 201 * If non-NULL, use the specified cache for blocks. 202 * If null, leveldb will automatically create and use an 8MB internal cache. 203 * Default: null 204 */ 205 @property 206 void cache(Cache cache) 207 { 208 _cache = cache; // save pointer so gc doesn't collect it 209 if(cache) 210 leveldb_options_set_cache(_opt, cache.ptr); 211 else 212 leveldb_options_set_cache(_opt, null); 213 } 214 215 /** If non-NULL, use the specified filter policy to reduce disk reads. 216 * Many applications will benefit from passing the result of 217 * BloomFilterPolicy here. 218 * 219 * Default: null 220 */ 221 @property 222 void filter_policy(FilterPolicy filter) 223 { 224 _filter = filter; // save pointer so gc doesn't collect it 225 if(filter) 226 leveldb_options_set_filter_policy(_opt, filter.ptr); 227 else 228 leveldb_options_set_filter_policy(_opt, null); 229 } 230 231 /** Comparator used to define the order of keys in the table. 232 * Default: a comparator that uses lexicographic byte-wise ordering 233 * 234 * REQUIRES: The client must ensure that the comparator supplied 235 * here has the same name and orders keys *exactly* the same as the 236 * comparator provided to previous open calls on the same DB. 237 */ 238 @property 239 void comparator(Comparator comparator) 240 { 241 _comparator = comparator; // save pointer so gc doesn't collect it 242 if(comparator) 243 leveldb_options_set_comparator(_opt, comparator._comp); 244 else 245 leveldb_options_set_comparator(_opt, null); 246 } 247 248 /// indicates if the option has been created 249 @property 250 bool valid() inout 251 { 252 return _opt !is null; 253 } 254 255 // Optional Sub objects 256 257 /// Environment object, API only has deault environment 258 static class Environment 259 { 260 private: 261 leveldb_env_t _env; 262 263 public: 264 this() 265 { 266 if((_env = leveldb_create_default_env()) is null) 267 throw new LeveldbException("Failed to create leveldb environment"); 268 } 269 270 ~this() 271 { 272 leveldb_env_destroy(_env); 273 } 274 } 275 276 abstract static class Cache 277 { 278 @property 279 inout(leveldb_cache_t) ptr() inout; 280 } 281 282 /// Cache Object, can only set size 283 static class LRUCache : Cache 284 { 285 private: 286 leveldb_cache_t _cache; 287 288 public: 289 290 this() 291 { 292 /// Create a default cache 10MB 293 this(10 * 1048576); 294 } 295 296 this(size_t capacity) 297 { 298 if((_cache = leveldb_cache_create_lru(capacity)) is null) 299 throw new LeveldbException("Failed to create leveldb cache"); 300 } 301 302 ~this() 303 { 304 leveldb_cache_destroy(_cache); 305 } 306 307 @property 308 override inout(leveldb_cache_t) ptr() inout 309 { 310 return _cache; 311 } 312 } 313 314 abstract static class FilterPolicy 315 { 316 @property 317 inout(leveldb_filterpolicy_t) ptr() inout; 318 } 319 320 /// Bloom filter 321 static class BloomFilterPolicy : FilterPolicy 322 { 323 private: 324 leveldb_filterpolicy_t _filter; 325 326 public: 327 328 this() 329 { 330 /// Create a default bloom filter 331 this(10); 332 } 333 334 this(int bits_per_key) 335 { 336 if((_filter = leveldb_filterpolicy_create_bloom(bits_per_key)) is null) 337 throw new LeveldbException("Failed to create leveldb bloom filter"); 338 } 339 340 ~this() 341 { 342 leveldb_filterpolicy_destroy(_filter); 343 } 344 345 @property 346 override inout(leveldb_filterpolicy_t) ptr() inout 347 { 348 return _filter; 349 } 350 } 351 352 /// User Filter Policy 353 abstract static class AFilterPolicy : FilterPolicy 354 { 355 private: 356 leveldb_filterpolicy_t _filter; 357 358 public: 359 360 this() 361 { 362 if((_filter = leveldb_filterpolicy_create(cast(void*)this, 363 &filterDestructor, &filterCreate, &filterKeyMayMatch, &filterName)) is null) 364 throw new LeveldbException("Failed to create leveldb filter"); 365 } 366 367 ~this() 368 { 369 leveldb_filterpolicy_destroy(_filter); 370 } 371 372 void destructor(); 373 char* create(const const(char)* key_array, const size_t* key_length_array, 374 int num_keys, size_t* filter_length); 375 ubyte match(const char[]key, const char[] filter); 376 string name(); 377 378 @property 379 override inout(leveldb_filterpolicy_t) ptr() inout 380 { 381 return _filter; 382 } 383 } 384 385 /// User Comparator, this is a default string comparator 386 static class Comparator 387 { 388 private: 389 leveldb_comparator_t _comp; 390 391 public: 392 393 this() 394 { 395 if((_comp = leveldb_comparator_create(cast(void*)this, 396 &compareDestructor, &compareCompare, &compareName)) is null) 397 throw new LeveldbException("Failed to create leveldb comparator"); 398 } 399 400 ~this() 401 { 402 leveldb_comparator_destroy(_comp); 403 } 404 405 void destructor() inout 406 {} 407 408 int compare(const char[] a, const char[] b) inout 409 { 410 return cmp(a, b); 411 } 412 413 string name() inout 414 { 415 return "String Compare"; 416 } 417 } 418 } 419 420 package abstract class ASnapshot 421 { 422 public: 423 @property 424 inout(leveldb_snapshot_t) ptr() inout; 425 } 426 427 /// Controls database reading options 428 class ReadOptions 429 { 430 private: 431 leveldb_readoptions_t _opt; 432 433 package: 434 @property 435 inout(leveldb_readoptions_t) ptr() inout 436 { 437 return _opt; 438 } 439 440 public: 441 /// Create the internal option object 442 this() 443 { 444 if((_opt = leveldb_readoptions_create()) is null) 445 throw new LeveldbException("Failed to create an read options"); 446 } 447 448 /// Destroy any valid option pointer 449 ~this() 450 { 451 if(valid) 452 { 453 leveldb_readoptions_destroy(_opt); 454 _opt = null; 455 } 456 } 457 458 /** If true, all data read from underlying storage will be 459 * verified against corresponding checksums. 460 * Default: false 461 */ 462 @property 463 void verify_checksums(bool val) 464 { 465 leveldb_readoptions_set_verify_checksums(_opt, val); 466 } 467 468 /** Should the data read for this iteration be cached in memory? 469 * Callers may wish to set this field to false for bulk scans. 470 * Default: true 471 */ 472 @property 473 void fill_cache(bool val) 474 { 475 leveldb_readoptions_set_fill_cache(_opt, val); 476 } 477 478 /** If "snapshot" is non-NULL, read as of the supplied snapshot 479 * (which must belong to the DB that is being read and which must 480 * not have been released). If "snapshot" is null, use an impliicit 481 * snapshot of the state at the beginning of this read operation. 482 * Default: null 483 */ 484 @property 485 void snapshot(const(ASnapshot) snapshot) 486 { 487 leveldb_readoptions_set_snapshot(_opt, snapshot.ptr); 488 } 489 490 /// indicates if the option has been created 491 @property 492 bool valid() inout 493 { 494 return _opt !is null; 495 } 496 } 497 498 /// Controls db writting 499 class WriteOptions 500 { 501 private: 502 leveldb_writeoptions_t _opt; 503 504 package: 505 @property 506 inout(leveldb_writeoptions_t) ptr() inout 507 { 508 return _opt; 509 } 510 511 public: 512 /// Create the internal option object 513 this() 514 { 515 if((_opt = leveldb_writeoptions_create()) is null) 516 throw new LeveldbException("Failed to create an read options"); 517 } 518 519 /// Destroy any valid option pointer 520 ~this() 521 { 522 if(valid) 523 { 524 leveldb_writeoptions_destroy(_opt); 525 _opt = null; 526 } 527 } 528 529 /** If true, the write will be flushed from the operating system 530 * buffer cache (by calling WritableFile::Sync()) before the write 531 * is considered complete. If this flag is true, writes will be 532 * slower. 533 * 534 * If this flag is false, and the machine crashes, some recent 535 * writes may be lost. Note that if it is just the process that 536 * crashes (i.e., the machine does not reboot), no writes will be 537 * lost even if sync==false. 538 * 539 * In other words, a DB write with sync==false has similar 540 * crash semantics as the "write()" system call. A DB write 541 * with sync==true has similar crash semantics to a "write()" 542 * system call followed by "fsync()". 543 * 544 * Default: false 545 */ 546 @property 547 void sync(bool val) 548 { 549 leveldb_writeoptions_set_sync(_opt, val); 550 } 551 552 /// indicates if the option has been created 553 @property 554 bool valid() inout 555 { 556 return _opt !is null; 557 } 558 } 559 560 //* Leveldb C API Callback handlers */ 561 private: 562 extern(C): 563 void compareDestructor(void* state) 564 { 565 auto c = cast(Options.Comparator*)state; 566 c.destructor(); 567 } 568 569 int compareCompare(void* state, const char* a, size_t alen, const char* b, size_t blen) 570 { 571 auto c = cast(Options.Comparator*)state; 572 return c.compare(a[0..alen], b[0..blen]); 573 } 574 575 const(char*) compareName(void* state) 576 { 577 auto c = cast(Options.Comparator*)state; 578 return toStringz(c.name()); 579 } 580 581 void filterDestructor(void* state) 582 { 583 auto f = cast(Options.AFilterPolicy*)state; 584 f.destructor(); 585 } 586 587 char* filterCreate(void* state, const const(char)* key_array, 588 const size_t* key_length_array, int num_keys, size_t* filter_length) 589 { 590 auto f = cast(Options.AFilterPolicy*)state; 591 return f.create(key_array, key_length_array, num_keys, filter_length); 592 } 593 594 ubyte filterKeyMayMatch(void* state, const char* key, size_t length, const char* filter, 595 size_t filter_length) 596 { 597 auto f = cast(Options.AFilterPolicy*)state; 598 return f.match(key[0..length], filter[0..filter_length]); 599 } 600 601 const(char*) filterName(void* state) 602 { 603 auto f = cast(Options.AFilterPolicy*)state; 604 return toStringz(f.name()); 605 }