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     }