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: 18 import std.algorithm : cmp; 19 import std.string : toStringz; 20 21 import deimos.leveldb.leveldb, 22 leveldb.exceptions; 23 24 /// Create default init read and write options 25 public: 26 __gshared const(ReadOptions) DefaultReadOptions; 27 __gshared const(WriteOptions) DefaultWriteOptions; 28 29 shared static this() 30 { 31 DefaultReadOptions = new ReadOptions; 32 DefaultWriteOptions = new WriteOptions; 33 } 34 35 /** 36 * Database creation and general usgage options 37 */ 38 class Options 39 { 40 private: 41 leveldb_options_t _opt = null; 42 43 /// Store a copy of these objects so they are not cleaned up by the GC 44 Environment _env; 45 Cache _cache; 46 FilterPolicy _filter; 47 Comparator _comparator; 48 49 package: 50 @property 51 inout(leveldb_options_t) ptr() inout 52 { 53 return _opt; 54 } 55 56 public: 57 58 /// Create the internal option object 59 this() 60 { 61 _opt = dbEnforce(leveldb_options_create(), "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 _env = dbEnforce(leveldb_create_default_env(), "Failed to create leveldb environment"); 267 } 268 269 ~this() 270 { 271 leveldb_env_destroy(_env); 272 } 273 } 274 275 abstract static class Cache 276 { 277 @property 278 inout(leveldb_cache_t) ptr() inout; 279 } 280 281 /// Cache Object, can only set size 282 static class LRUCache : Cache 283 { 284 private: 285 leveldb_cache_t _cache; 286 287 public: 288 289 this() 290 { 291 /// Create a default cache 10MB 292 this(10 * 1048576); 293 } 294 295 this(size_t capacity) 296 { 297 _cache = dbEnforce(leveldb_cache_create_lru(capacity), "Failed to create leveldb cache"); 298 } 299 300 ~this() 301 { 302 leveldb_cache_destroy(_cache); 303 } 304 305 @property 306 override inout(leveldb_cache_t) ptr() inout 307 { 308 return _cache; 309 } 310 } 311 312 abstract static class FilterPolicy 313 { 314 @property 315 inout(leveldb_filterpolicy_t) ptr() inout; 316 } 317 318 /// Bloom filter 319 static class BloomFilterPolicy : FilterPolicy 320 { 321 private: 322 leveldb_filterpolicy_t _filter; 323 324 public: 325 326 this() 327 { 328 /// Create a default bloom filter 329 this(10); 330 } 331 332 this(int bits_per_key) 333 { 334 _filter = dbEnforce(leveldb_filterpolicy_create_bloom(bits_per_key), "Failed to create leveldb bloom filter"); 335 } 336 337 ~this() 338 { 339 leveldb_filterpolicy_destroy(_filter); 340 } 341 342 @property 343 override inout(leveldb_filterpolicy_t) ptr() inout 344 { 345 return _filter; 346 } 347 } 348 349 /// User Filter Policy 350 abstract static class AFilterPolicy : FilterPolicy 351 { 352 private: 353 leveldb_filterpolicy_t _filter; 354 355 public: 356 357 this() 358 { 359 _filter = dbEnforce(leveldb_filterpolicy_create(cast(void*)this, &filterDestructor, &filterCreate, &filterKeyMayMatch, &filterName), "Failed to create leveldb filter"); 360 } 361 362 ~this() 363 { 364 leveldb_filterpolicy_destroy(_filter); 365 } 366 367 void destructor(); 368 char* create(const const(char)* key_array, const size_t* key_length_array, 369 int num_keys, size_t* filter_length); 370 ubyte match(const char[]key, const char[] filter); 371 string name(); 372 373 @property 374 override inout(leveldb_filterpolicy_t) ptr() inout 375 { 376 return _filter; 377 } 378 } 379 380 /// User Comparator, this is a default string comparator 381 static class Comparator 382 { 383 private: 384 leveldb_comparator_t _comp; 385 386 public: 387 388 this() 389 { 390 _comp = dbEnforce(leveldb_comparator_create(cast(void*)this, &compareDestructor, &compareCompare, &compareName), "Failed to create leveldb comparator"); 391 } 392 393 ~this() 394 { 395 leveldb_comparator_destroy(_comp); 396 } 397 398 void destructor() inout 399 {} 400 401 int compare(const char[] a, const char[] b) inout 402 { 403 return cmp(a, b); 404 } 405 406 string name() inout 407 { 408 return "String Compare"; 409 } 410 } 411 } 412 413 package abstract class ASnapshot 414 { 415 public: 416 @property 417 inout(leveldb_snapshot_t) ptr() inout; 418 } 419 420 /// Controls database reading options 421 class ReadOptions 422 { 423 private: 424 leveldb_readoptions_t _opt; 425 426 package: 427 @property 428 inout(leveldb_readoptions_t) ptr() inout 429 { 430 return _opt; 431 } 432 433 public: 434 /// Create the internal option object 435 this() 436 { 437 _opt = dbEnforce(leveldb_readoptions_create(),"Failed to create an read options"); 438 } 439 440 /// Destroy any valid option pointer 441 ~this() 442 { 443 if(valid) 444 { 445 leveldb_readoptions_destroy(_opt); 446 _opt = null; 447 } 448 } 449 450 /** If true, all data read from underlying storage will be 451 * verified against corresponding checksums. 452 * Default: false 453 */ 454 @property 455 void verify_checksums(bool val) 456 { 457 leveldb_readoptions_set_verify_checksums(_opt, val); 458 } 459 460 /** Should the data read for this iteration be cached in memory? 461 * Callers may wish to set this field to false for bulk scans. 462 * Default: true 463 */ 464 @property 465 void fill_cache(bool val) 466 { 467 leveldb_readoptions_set_fill_cache(_opt, val); 468 } 469 470 /** If "snapshot" is non-NULL, read as of the supplied snapshot 471 * (which must belong to the DB that is being read and which must 472 * not have been released). If "snapshot" is null, use an impliicit 473 * snapshot of the state at the beginning of this read operation. 474 * Default: null 475 */ 476 @property 477 void snapshot(const(ASnapshot) snapshot) 478 { 479 leveldb_readoptions_set_snapshot(_opt, snapshot.ptr); 480 } 481 482 /// indicates if the option has been created 483 @property 484 bool valid() inout 485 { 486 return _opt !is null; 487 } 488 } 489 490 /// Controls db writting 491 class WriteOptions 492 { 493 private: 494 leveldb_writeoptions_t _opt; 495 496 package: 497 @property 498 inout(leveldb_writeoptions_t) ptr() inout 499 { 500 return _opt; 501 } 502 503 public: 504 /// Create the internal option object 505 this() 506 { 507 _opt = dbEnforce(leveldb_writeoptions_create(), "Failed to create an read options"); 508 } 509 510 /// Destroy any valid option pointer 511 ~this() 512 { 513 if(valid) 514 { 515 leveldb_writeoptions_destroy(_opt); 516 _opt = null; 517 } 518 } 519 520 /** If true, the write will be flushed from the operating system 521 * buffer cache (by calling WritableFile::Sync()) before the write 522 * is considered complete. If this flag is true, writes will be 523 * slower. 524 * 525 * If this flag is false, and the machine crashes, some recent 526 * writes may be lost. Note that if it is just the process that 527 * crashes (i.e., the machine does not reboot), no writes will be 528 * lost even if sync==false. 529 * 530 * In other words, a DB write with sync==false has similar 531 * crash semantics as the "write()" system call. A DB write 532 * with sync==true has similar crash semantics to a "write()" 533 * system call followed by "fsync()". 534 * 535 * Default: false 536 */ 537 @property 538 void sync(bool val) 539 { 540 leveldb_writeoptions_set_sync(_opt, val); 541 } 542 543 /// indicates if the option has been created 544 @property 545 bool valid() inout 546 { 547 return _opt !is null; 548 } 549 } 550 551 //* Leveldb C API Callback handlers */ 552 private: 553 extern(C): 554 void compareDestructor(void* state) 555 { 556 auto c = cast(Options.Comparator*)state; 557 c.destructor(); 558 } 559 560 int compareCompare(void* state, const char* a, size_t alen, const char* b, size_t blen) 561 { 562 auto c = cast(Options.Comparator*)state; 563 return c.compare(a[0..alen], b[0..blen]); 564 } 565 566 const(char*) compareName(void* state) 567 { 568 auto c = cast(Options.Comparator*)state; 569 return toStringz(c.name()); 570 } 571 572 void filterDestructor(void* state) 573 { 574 auto f = cast(Options.AFilterPolicy*)state; 575 f.destructor(); 576 } 577 578 char* filterCreate(void* state, const const(char)* key_array, 579 const size_t* key_length_array, int num_keys, size_t* filter_length) 580 { 581 auto f = cast(Options.AFilterPolicy*)state; 582 return f.create(key_array, key_length_array, num_keys, filter_length); 583 } 584 585 ubyte filterKeyMayMatch(void* state, const char* key, size_t length, const char* filter, 586 size_t filter_length) 587 { 588 auto f = cast(Options.AFilterPolicy*)state; 589 return f.match(key[0..length], filter[0..filter_length]); 590 } 591 592 const(char*) filterName(void* state) 593 { 594 auto f = cast(Options.AFilterPolicy*)state; 595 return toStringz(f.name()); 596 }