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     }