1 /* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * D translation by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
18 // CDB key/value database reader
19 module iv
.tinycdb
/*is aliced*/;
25 enum Version
{ Major
= 0, Minor
= 78 }
28 int mFD
= -1; /* file descriptor */
29 bool mCloseFD
; /* true: close fd on close() */
30 uint mFSize
; /* datafile size */
31 uint mDataEnd
; /* end of data ptr */
32 const(ubyte)* mDataPtr
; /* mmap'ed file memory */
36 @disable this (this); // no copying
38 this (string fname
) { open(fname
); }
40 bool open (string fname
) {
41 import std
.string
: toStringz
;
42 import core
.sys
.posix
.fcntl
: xopen
= open
, O_RDONLY
;
44 int fd
= xopen(fname
.toStringz
, O_RDONLY
);
47 if (open(fd
)) return true;
48 import core
.sys
.posix
.unistd
: xclose
= close
;
62 this (int fd
) @nogc { open(fd
); }
64 @property bool opened () const pure @safe { return (mFD
>= 0 && mDataPtr
!is null); }
68 import core
.sys
.posix
.sys
.mman
: mmap
, PROT_READ
, MAP_SHARED
, MAP_FAILED
;
69 import core
.sys
.posix
.sys
.stat
: fstat
, stat_t
;
75 if (fd
< 0 ||
fstat(fd
, &st
) < 0) return false;
76 /* trivial sanity check: at least toc should be here */
77 if (st
.st_size
< 2048) return false;
78 fsize
= (st
.st_size
< 0xffffffffu ?
cast(uint)st
.st_size
: 0xffffffffu
);
80 mem
= cast(ubyte*)mmap(null, fsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
81 if (mem
== MAP_FAILED
) return false;
85 /* set madvise() parameters. Ignore errors for now if system doesn't support it */
87 import core
.sys
.posix
.sys
.mman
: posix_madvise
, POSIX_MADV_WILLNEED
, POSIX_MADV_RANDOM
;
88 posix_madvise(mem
, 2048, POSIX_MADV_WILLNEED
);
89 posix_madvise(mem
+2048, mFSize
-2048, POSIX_MADV_RANDOM
);
92 if (dend
< 2048) dend
= 2048; else if (dend
>= fsize
) dend
= fsize
;
99 import core
.sys
.posix
.sys
.mman
: munmap
;
100 munmap(cast(void*)mDataPtr
, mFSize
);
102 bool wasError
= false;
104 import core
.sys
.posix
.unistd
: xclose
= close
;
105 wasError
= (xclose(mFD
) != 0);
118 const(char)[] opIndex (const(char)[] key
) const { return find(key
); }
120 // null: not found (or some error occured)
121 const(T
)[] find(T
=char) (const(void)[] key
) const
122 if (is(T
== char) ||
is(T
== byte) ||
is(T
== ubyte) ||
is(T
== void))
124 uint httodo
; /* ht bytes left to look */
126 if (key
.length
< 1 || key
.length
>= mDataEnd
) return null; /* if key size is too small or too large */
127 immutable klen
= cast(uint)key
.length
;
128 immutable hval
= hash(key
);
129 /* find (pos,n) hash table to use */
130 /* first 2048 bytes (toc) are always available */
132 auto htp
= cast(const(ubyte)*)mDataPtr
+((hval
<<3)&2047); /* index in toc (256x8) */
133 n
= unpack(htp
+4); /* table size */
134 if (!n
) return null; /* empty table: not found */
135 httodo
= n
<<3; /* bytes of htab to lookup */
136 pos
= unpack(htp
); /* htab position */
137 if (n
> (mFSize
>>3) ||
/* overflow of httodo ? */
138 pos
< mDataEnd ||
/* is htab inside data section ? */
139 pos
> mFSize ||
/* htab start within file ? */
140 httodo
> mFSize
-pos
) /* entrie htab within file ? */
141 return null; // error
142 auto htab
= mDataPtr
+pos
; /* hash table */
143 auto htend
= htab
+httodo
; /* after end of hash table */
144 /* htab starting position: rest of hval modulo htsize, 8bytes per elt */
145 htp
= htab
+(((hval
>>8)%n
)<<3); /* hash table pointer */
147 pos
= unpack(htp
+4); /* record position */
148 if (!pos
) return null;
149 if (unpack(htp
) == hval
) {
150 if (pos
> mDataEnd
-8) return null; /* key+val lengths: error */
151 if (unpack(mDataPtr
+pos
) == klen
) {
152 import core
.stdc
.string
: memcmp
;
153 if (mDataEnd
-klen
< pos
+8) return null; // error
154 if (memcmp(key
.ptr
, mDataPtr
+pos
+8, klen
) == 0) {
155 n
= unpack(mDataPtr
+pos
+4);
157 if (mDataEnd
< n || mDataEnd
-n
< pos
+klen
) return /*errno = EPROTO, -1*/null; // error
158 // key: [pos..pos+klen]
159 // val: [pos+klen..pos+klen+n]
160 return cast(const(T
)[])mDataPtr
[pos
+klen
..pos
+klen
+n
];
165 if (!httodo
) return null;
166 if ((htp
+= 8) >= htend
) htp
= htab
;
170 //WARNING! returned range should not outlive this object!
171 auto findFirst(T
=char) (const(void)[] key
) const nothrow @nogc
172 if (is(T
== char) ||
is(T
== byte) ||
is(T
== ubyte) ||
is(T
== void))
178 const(ubyte)* htp
, htab
, htend
;
188 @property bool empty () const @safe pure { return (cdbp
is null ||
!cdbp
.opened
); }
189 @property const(T
)[] front () const @trusted pure nothrow @nogc {
190 return (empty ?
null : cast(const(T
)[])cdbp
.mDataPtr
[vpos
..vpos
+vlen
]);
192 void close () { cdbp
= null; key
= null; htp
= htab
= htend
= null; }
197 immutable uint klen
= cast(uint)key
.length
;
200 if (!pos
) { close(); return; }
201 n
= (unpack(htp
) == hval
);
202 if ((htp
+= 8) >= htend
) htp
= htab
;
205 if (pos
> cdb
.mFSize
-8) { close(); return; }
206 if (unpack(cdb
.mDataPtr
+pos
) == klen
) {
207 import core
.stdc
.string
: memcmp
;
208 if (cdb
.mFSize
-klen
< pos
+8) { close(); return; }
209 if (memcmp(key
.ptr
, cdb
.mDataPtr
+pos
+8, klen
) == 0) {
210 n
= unpack(cdb
.mDataPtr
+pos
+4);
212 if (cdb
.mFSize
< n || cdb
.mFSize
-n
< pos
+klen
) { close(); return; }
213 // key: [pos..pos+klen]
214 // val: [pos+klen..pos+klen+n]
226 if (key
.length
< 1 || key
.length
>= mDataEnd
) return Iter
.init
; /* if key size is too large */
227 immutable klen
= cast(uint)key
.length
;
234 it
.htp
= mDataPtr
+((it
.hval
<<3)&2047);
235 uint n
= unpack(it
.htp
+4);
237 if (!n
) return Iter
.init
;
238 uint pos
= unpack(it
.htp
);
239 if (n
> (mFSize
>> 3) ||
242 it
.httodo
> mFSize
-pos
)
245 it
.htab
= mDataPtr
+pos
;
246 it
.htend
= it
.htab
+it
.httodo
;
247 it
.htp
= it
.htab
+(((it
.hval
>>8)%n
)<<3);
249 it
.popFront(); // prepare first item
254 uint hash() (const(void)[] buf
) {
255 auto p
= cast(const(ubyte)*)buf
.ptr
;
256 uint hash
= 5381; /* start value */
257 foreach (immutable nothing
; 0..buf
.length
) hash
= (hash
+(hash
<<5))^
*p
++;
262 uint unpack() (const(ubyte)* buf
) {
263 //assert(buf !is null);
265 n
<<= 8; n |
= buf
[2];
266 n
<<= 8; n |
= buf
[1];
267 n
<<= 8; n |
= buf
[0];