1 /**
2 Copyright: Copyright (c) 2016 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.world.worlddb;
7 
8 import std.experimental.allocator.mallocator;
9 import voxelman.world.db.lmdbworlddb;
10 import voxelman.world.db.sqliteworlddb;
11 
12 version(Windows) {
13 	version = Lmdb;
14 } else {
15 	version = Sqlite;
16 }
17 
18 final class WorldDb
19 {
20 	version(Lmdb) LmdbWorldDb db;
21 	version(Sqlite) SqliteWorldDb db;
22 	private ubyte[] buffer;
23 
24 	//-----------------------------------------------
25 	void open(string filename) {
26 		version(Lmdb) mdb_load_libs();
27 		db.open(filename);
28 	}
29 	void close() {
30 		db.close();
31 		if (buffer !is null) Mallocator.instance.deallocate(buffer);
32 	}
33 
34 	ubyte[] tempBuffer() @property {
35 		if (buffer is null) buffer = cast(ubyte[])Mallocator.instance.allocate(4096*64);
36 
37 		return buffer;
38 	}
39 
40 	//-----------------------------------------------
41 	version(Lmdb) {
42 		private enum Table : ulong
43 		{
44 			world,
45 			dimention,
46 			region,
47 			chunk,
48 		}
49 		void putPerWorldValue(K)(K key, ubyte[] value) {
50 			put(formKey(key), Table.world, value);
51 		}
52 		ubyte[] getPerWorldValue(K)(K key) {
53 			return get(formKey(key), Table.world);
54 		}
55 		void putPerChunkValue(ulong key, ubyte[] value) {
56 			put(key, Table.chunk, value);
57 		}
58 		ubyte[] getPerChunkValue(ulong key) {
59 			return get(key, Table.chunk);
60 		}
61 
62 		private void put(ulong key, ulong table, ubyte[] value) {
63 			ubyte[16] dbKey;
64 			(*cast(ulong[2]*)dbKey.ptr)[0] = key;
65 			(*cast(ulong[2]*)dbKey.ptr)[1] = table;
66 			db.put(dbKey, value);
67 		}
68 		private ubyte[] get(ulong key, ulong table) {
69 			ubyte[16] dbKey;
70 			(*cast(ulong[2]*)dbKey.ptr)[0] = key;
71 			(*cast(ulong[2]*)dbKey.ptr)[1] = table;
72 			return db.get(dbKey);
73 		}
74 		private void del(ulong key, ulong table) {
75 			ubyte[16] dbKey;
76 			(*cast(ulong[2]*)dbKey.ptr)[0] = key;
77 			(*cast(ulong[2]*)dbKey.ptr)[1] = table;
78 			return db.del(dbKey);
79 		}
80 	}
81 
82 	version(Sqlite) {
83 		void putPerWorldValue(K)(K key, ubyte[] value) {
84 			db.savePerWorldData(key, value);
85 		}
86 		ubyte[] getPerWorldValue(K)(K key) {
87 			return db.loadPerWorldData(key);
88 		}
89 		void putPerChunkValue(ulong key, ubyte[] value) {
90 			db.savePerChunkData(key, value);
91 		}
92 		ubyte[] getPerChunkValue(ulong key) {
93 			return db.loadPerChunkData(key);
94 		}
95 	}
96 
97 	//-----------------------------------------------
98 	void beginTxn() {
99 		db.beginTxn();
100 	}
101 	void abortTxn() {
102 		db.abortTxn();
103 	}
104 	void commitTxn() {
105 		db.commitTxn();
106 	}
107 }
108 
109 ulong formKey(K)(K _key) @nogc
110 	if (is(K == string) || is(K == ulong))
111 {
112 	static if (is(K == string))
113 		return hashBytes(cast(ubyte[])_key);
114 	else static if (is(K == ulong))
115 		return _key;
116 }
117 
118 ulong hashBytes(ubyte[] bytes) @nogc
119 {
120     ulong hash = 5381;
121 
122     foreach(c; bytes)
123         hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
124 
125     return hash;
126 }