1 /*
2    bdb2d is BerkeleyDB for D language
3    It is part of unDE project (http://unde.su)
4 
5    Copyright (C) 2009-2014 Nikolay (unDEFER) Krivchenkov <undefer@gmail.com>
6 
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 module berkeleydb.dbt;
22 
23 import berkeleydb.c;
24 import berkeleydb.dbexception;
25 import std.array;
26 import std.traits;
27 import core.memory;
28 
29 private inout(ubyte[]) tobytes(T)(inout ref T arg)
30 if (__traits(hasMember, arg, "sizeof"))
31 {
32 
33 	static if(__traits(hasMember, arg, "ptr"))
34 	{
35 		auto size = arg.length * typeof(arg[0]).sizeof;
36 		return (cast(ubyte*)arg.ptr)[0..size];
37 	}
38 	else
39 	{
40 		auto size = arg.sizeof;
41 		return (cast(ubyte*)&arg)[0..size];
42 	}
43 }
44 
45 private inout(T) frombytes(T)(inout ubyte[] arg)
46 if (!isPointer!(T) && !isArray!(T) &&
47 		__traits(hasMember, T, "sizeof") &&
48 		!__traits(hasMember, T, "ptr"))
49 {
50 	assert(T.sizeof == arg.length, "Casting Dbt to structure with not corresponding size");
51 
52 	T *res = cast(T*) arg;
53 
54 	return *res;
55 }
56 
57 private inout(T*) frombytes(A: T*, T)(inout ubyte[] arg)
58 if (__traits(hasMember, T, "sizeof") &&
59 		!__traits(hasMember, T, "ptr"))
60 {
61 	assert(T.sizeof == arg.length, "Casting Dbt to structure with not corresponding size");
62 	return cast(T*) arg;
63 }
64 
65 private inout(T[]) frombytes(A : T[], T)(inout ubyte[] arg)
66 if (__traits(hasMember, T, "sizeof"))
67 {
68 	assert((arg.length % T.sizeof) == 0, "Casting Dbt to array with not corresponding entries size");
69 
70 	return (cast(T*)arg)[0..arg.length/T.sizeof];
71 }
72 
73 private inout(ubyte[]) dbttobytes(inout ref DBT dbt)
74 {
75 	return (cast(inout ubyte*)dbt.data)[0..dbt.size];
76 }
77 
78 struct Dbt
79 {
80 	DBT dbt;
81 	alias dbt this;
82 
83 	this(T)(ref T arg)
84 	{
85 		opAssign(arg);
86 	}
87 
88 	void opAssign(T)(ref T arg)
89 	if (__traits(hasMember, arg, "sizeof"))
90 	{
91 		static if(__traits(hasMember, arg, "ptr"))
92 		{
93 			size = cast(uint) (arg.length * typeof(arg[0]).sizeof);
94 			data = cast(void*) arg.ptr;
95 		}
96 		else
97 		{
98 			size = arg.sizeof;
99 			data = cast(void*) &arg;
100 		}
101 	}
102 
103 	inout(T) to(T)() inout
104 	{
105 		return frombytes!T(dbttobytes(dbt));
106 	}
107 }
108 
109 static assert(DBT.sizeof == Dbt.sizeof);
110 
111 unittest
112 {
113 	string hello = "Hello, world!";
114 
115 	Dbt dbt = hello;
116 	assert(dbt.size == hello.length);
117 
118 	assert(dbt.to!string() == "Hello, world!");
119 	assert(dbt.to!string() is "Hello, world!");
120 
121 	struct S
122 	{
123 		int a;
124 		byte b;
125 	}
126 	
127 	S s;
128 	s.a = 5000;
129 	s.b = 22;
130 
131 	Dbt dbt_struct;
132     dbt_struct = s;
133 	assert( dbt_struct.size == s.sizeof );
134 	assert( dbt_struct.to!S() == s );
135 
136 	S *ps = dbt_struct.to!(S*)();
137 	assert( *ps == s );
138 	assert( ps is &s );
139 }
140 
141 struct UserMemDbt
142 {
143 	DBT dbt = DBT(null, 0, 0, 0, 0, null, DB_DBT_USERMEM);
144 	alias dbt this;
145 
146     invariant()
147     {
148         assert(dbt.flags & DB_DBT_USERMEM);
149     }
150 
151     this(ref Dbt dbt)
152     {
153         if (!(dbt.flags & DB_DBT_USERMEM))
154         {
155             throw new DbWrongUsingException("Constructing UserMemDbt from Dbt variable without DB_DBT_USERMEM in flags");
156         }
157         this.dbt = dbt;
158     }
159 
160 	this(T)(ref T arg)
161 	{
162 		opAssign(arg);
163 	}
164 	
165 	this(T)(int size, ref T arg)
166 	in {
167 		assert(size > 0);
168 	}
169 	body {
170 		ulen = size;
171 		data = GC.malloc(size);
172 		opAssign(arg);
173 	}
174 	
175 	this()(int size)
176 	in {
177 		assert(size > 0);
178 	}
179 	body {
180 		ulen = size;
181 		data = GC.malloc(size);
182 	}
183 
184 	/*~this()
185 	{
186 		GC.free(data);
187 	}*/
188 
189 	void opAssign(T)(ref T arg)
190 	if (__traits(hasMember, arg, "sizeof"))
191 	{
192 
193 		static if(__traits(hasMember, arg, "ptr"))
194 		{
195 			size = cast(uint)(arg.length * typeof(arg[0]).sizeof);
196 
197 			if (ulen == 0)
198 			{
199 				ulen = size;
200 				data = GC.malloc(size);
201 			}
202 
203 			assert(size <= ulen);
204 			(cast(char*) data)[0..size] = 
205 				(cast(char*) arg.ptr)[0..size];
206 		}
207 		else
208 		{
209 			size = arg.sizeof;
210 
211 			if (ulen == 0)
212 			{
213 				ulen = size;
214 				data = GC.malloc(size);
215 			}
216 
217 			assert(size <= ulen);
218 			(cast(char*) data)[0..size] = 
219 				(cast(char*) &arg)[0..size];
220 		}
221 	}
222 
223 	inout(T) to(T)() inout
224 	{
225 		return frombytes!T(dbttobytes(dbt));
226 	}
227 }
228 
229 unittest
230 {
231 	string hello = "Hello, world!";
232 
233 	UserMemDbt dbt = UserMemDbt(20, hello);
234 	assert(dbt.size == hello.length);
235 
236 	assert(dbt.to!string() == "Hello, world!");
237 	assert(dbt.to!string() !is "Hello, world!");
238 
239 	struct S
240 	{
241 		int a;
242 		byte b;
243 	}
244 	
245 	S s;
246 	s.a = 5000;
247 	s.b = 22;
248 
249     dbt = s;
250 	assert( dbt.size == s.sizeof );
251 	assert( dbt.to!S() == s );
252 
253 	S *ps = dbt.to!(S*)();
254 	assert( *ps == s );
255 	assert( ps !is &s );
256 }
257 
258 import std.stdint;
259 import std.exception;
260 import std.range;
261 import std.typecons;
262 
263 struct BulkDbt
264 {
265 	DBT dbt = DBT(null, 0, 0, 0, 0, null, DB_DBT_BULK | DB_DBT_USERMEM);
266 	alias dbt this;
267 	uint32_t *pointer;
268 
269     invariant()
270     {
271         assert(dbt.flags & DB_DBT_BULK);
272     }
273 
274 	struct Range(T) {
275 		uint32_t *pointer;
276 		DBT *dbt;
277 
278 		private this(BulkDbt* b) { 
279 			dbt = &b.dbt;
280 			pointer = cast(uint32_t*)(cast(ubyte*)dbt.data + 
281 					dbt.ulen) - 1;
282 		}
283 
284 		/// Forward range primitives.
285 		@property bool empty() const { 
286 			return !pointer || *pointer == -1; 
287 		}
288 		/// ditto
289 		@property Range save() { return this; }
290 
291 		/// ditto
292 		@property T front() 
293 		{ 
294 			uint32_t *__p = pointer;
295 			if (*__p == -1) return null;
296 
297 			ubyte *retdata = cast(uint8_t*)dbt.data + *__p--;
298 			uint32_t retdlen = *__p--;
299 
300 			return frombytes!T(retdata[0..retdlen]); 
301 		}
302 
303 		/// ditto
304 		void popFront()
305 		{
306 			enforce(pointer);
307 			pointer -= 2;
308 		}
309 
310 		T moveFront()
311 		{
312 			enforce(pointer);
313 			return front();
314 		}
315 	}
316 
317 	struct KeyRange(K, V) {
318 		uint32_t *pointer;
319 		DBT *dbt;
320 
321 		struct KeyValuePair{
322 			K key;
323 			V value;
324 
325 			bool opEquals(KeyValuePair arg)
326 			{
327 				return key == arg.key && value == arg.value;
328 			}
329 
330 		};
331 
332 		alias KeyValuePair KV;
333 
334 		private this(BulkDbt* b) { 
335 			dbt = &b.dbt;
336 			pointer = cast(uint32_t*)(cast(ubyte*)dbt.data + 
337 					dbt.ulen) - 1;
338 		}
339 
340 		/// Forward range primitives.
341 		@property bool empty() const { 
342 			return !pointer || *pointer == -1; 
343 		}
344 		/// ditto
345 		@property KeyRange save() { return this; }
346 
347 		private @property T _front(T)() 
348 		{ 
349 			uint32_t *__p = pointer;
350 			if (*__p == -1) return null;
351 
352 			ubyte *retdata = cast(uint8_t*)dbt.data + *__p--;
353 			uint32_t retdlen = *__p--;
354 
355 			return frombytes!T(retdata[0..retdlen]); 
356 		}
357 
358 		/// ditto
359 		@property KV front() 
360 		{ 
361 			KV res;
362 			res.key = _front!K();
363 			pointer -= 2;
364 			res.value = _front!V();
365 			pointer += 2;
366 
367 			return res; 
368 		}
369 
370 		/// ditto
371 		void popFront()
372 		{
373 			enforce(pointer);
374 			pointer -= 4;
375 		}
376 
377 		KV moveFront()
378 		{
379 			enforce(pointer);
380 			return front();
381 		}
382 	}
383 
384 	Range!(ubyte[]) opSlice()
385 	{
386 		return Range!(ubyte[])(&this);
387 	}
388 
389 	Range!(T) range(T)()
390 	{
391 		return Range!(T)(&this);
392 	}
393 
394 	KeyRange!(K, V) keyrange(K, V)()
395 	{
396 		return KeyRange!(K, V)(&this);
397 	}
398 
399     void set_pointer()
400     {
401 		pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1;
402         while (*pointer != -1)
403         {
404             pointer -= 2;
405             if (pointer < data)
406             {
407                 throw new DbWrongUsingException("Constructing BulkDbt from wrong DBT");
408             }
409         }
410     }
411 
412     this(ref Dbt dbt)
413     {
414         if (!(dbt.flags & DB_DBT_BULK))
415         {
416             throw new DbWrongUsingException("Constructing BulkDbt from Dbt variable without DB_DBT_BULK in flags");
417         }
418         this.dbt = dbt;
419         set_pointer();
420     }
421 
422 	this(T)(int size, ref T arg)
423 	if (isForwardRange!T || is(typeof(arg.byKey())))
424 	in {
425 		assert(size > 0);
426 	}
427 	body {
428 		ulen = size;
429 		data = GC.malloc(size);
430 		pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1;
431 		*pointer = -1;
432 		insertFronts(arg);
433 	}
434 
435 	this()(int size)
436 	in {
437 		assert(size > 0);
438 	}
439 	body {
440 		ulen = size;
441 		data = GC.malloc(size);
442 		pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1;
443 		*pointer = -1;
444 	}
445 
446 	
447 	/*~this()
448 	{
449 		GC.free(data);
450 	}*/
451 
452 	@property bool empty() const
453 	{
454 		return pointer is null;
455 	}
456 
457 	/**
458 	  Duplicates the container. 
459 
460 	  Complexity: $(BIGOH n).
461 	 */
462 	@property BulkDbt dup()
463 	{
464         auto t = this[];
465 		return BulkDbt(ulen, t);
466 	}
467 
468 	/**
469 	  Forward to $(D opSlice().front).
470 
471 	  Complexity: $(BIGOH 1)
472 	 */
473 	@property ubyte[] front()
474 	{
475 		uint32_t *__p = pointer;
476 		if (*__p == -1) return null;
477 
478 		ubyte *retdata = cast(uint8_t*)dbt.data + *__p--;
479 		uint32_t retdlen = *__p--;
480 
481 		return retdata[0..retdlen]; 
482 	}
483 
484 	/**
485 	  Inserts $(D stuff) to the front of the container. 
486 
487 	  Returns: The number of elements inserted
488 
489 	  Complexity: $(BIGOH log(n))
490 	 */
491 	size_t insertFronts(Stuff)(Stuff stuff)
492 	if (isForwardRange!Stuff && is(typeof(tobytes(stuff.front))) ||
493 				is(typeof(stuff.front): ubyte[]))
494 	{
495 		size_t result;
496 		foreach (item; stuff)
497 		{
498 			insertFront(item);
499 			++result;
500 		}
501 		return result;
502 	}
503 
504 	/// ditto
505 	size_t insertFront(T)(T _value)
506 	if (is(typeof(tobytes(_value))))
507 	{
508 		ubyte[] value = tobytes(_value);
509 
510 		uint32_t *__p = pointer;
511 		uint32_t __off = (pointer == 
512 		  (cast(uint32_t*)(cast(ubyte*)dbt.data + dbt.ulen) - 1)) 
513 			?  0 : __p[1] + __p[2];
514 		assert(__p && *__p == -1);
515 
516 		auto writedlen = value.length * typeof(value[0]).sizeof;
517 
518 		if ((cast(ubyte*)dbt.data + __off + writedlen) >
519 				cast(ubyte*)(__p - 2))
520 			throw new DbWrongUsingException("BulkDbt overflow");
521 		else {
522 			ubyte *writedata = cast(uint8_t*)dbt.data + __off;
523 			writedata[0..writedlen] = value[0..writedlen];
524 			__p[0] = __off;
525 			__p[-1] = cast(uint32_t)(writedlen);
526 			__p[-2] = cast(uint32_t)-1;
527 			pointer = __p - 2;
528 		}
529 
530 		return 1; 
531 	}
532 
533 	size_t insertFronts(K, V)(Tuple!(K, V) stuff)
534 	{
535 		size_t result;
536 		insertFront(stuff[0]);
537 		++result;
538 		insertFront(stuff[1]);
539 		++result;
540 		return result;
541 	}
542 
543 	size_t insertFronts(Stuff)(ref Stuff stuff)
544 	if (is(typeof(stuff.byKey())))
545 	{
546 		size_t result;
547 		foreach (key; stuff.byKey())
548 		{
549 			insertFront(key);
550 			++result;
551 			insertFront(stuff[key]);
552 			++result;
553 		}
554 		return result;
555 	}
556 	
557 	/// ditto
558 	alias insertFront insert;
559 
560 	/// ditto
561 	alias insert stableInsert;
562 
563 	/// ditto
564 	alias insertFront stableInsertFront;
565 
566 	/**
567 	  Removes the value at the front of the container. 
568 
569 	  Precondition: $(D !empty)
570 
571 	  Complexity: $(BIGOH 1).
572 	 */
573 	void removeFront()
574 	{
575 		enforce(pointer);
576 		uint32_t *__p = pointer;
577 
578 		if ((cast(ubyte*)dbt.data + ulen) == cast(ubyte*)__p)
579 			throw new DbWrongUsingException("BulkDbt underflow");
580 
581 		__p[0] = 0;
582 		__p[1] = 0;
583 		__p[2] = -1;
584 		pointer += 2;
585 	}
586 
587 	/// ditto
588 	alias removeFront stableRemoveFront;
589 
590 	/**
591 	  Removes $(D howMany) values at the front or back of the
592 	  container. 
593 
594 Returns: The number of elements removed
595 
596 Complexity: $(BIGOH howMany * log(n)).
597      */
598 	size_t removeFront(size_t howMany)
599 	{
600 		size_t result;
601 		while (result < howMany)
602 		{
603 			removeFront();
604 			++result;
605 		}
606 		return result;
607 	}
608 
609 	/// ditto
610 	alias removeFront stableRemoveFront;
611 }
612 
613 unittest
614 {
615     auto ar = ["Hello", "World!", "The piece"];
616 	BulkDbt dbt = BulkDbt(100, ar);
617 
618 	assert(dbt.range!(string).front == "Hello");
619 	assert(dbt.range!(string).drop(1).front == "World!");
620 	assert(dbt.range!(string).drop(2).front == "The piece");
621 	assert(dbt[].walkLength == 3);
622 
623 	string[string] map;
624 	map["Hello"] = "Hi!";
625 	map["Black"] = "White";
626 	map["Cat"] = "Dog";
627 
628 	BulkDbt keydbt = BulkDbt(120, map);
629     keydbt.insertFronts(tuple("I", "You"));
630 
631 	auto keyrange = keydbt.keyrange!(string, string);
632 	
633 	alias typeof(keyrange).KeyValuePair KeyValuePair;
634 
635     /*Map doesn't guarantee any order*/
636     int check( typeof(keyrange.front) value )
637     {
638         return value == KeyValuePair("Hello", "Hi!") ||
639             value == KeyValuePair("Cat", "Dog") ||
640             value == KeyValuePair("Black", "White");
641     }
642 
643 	assert(check(keyrange.front));
644 	assert(keyrange.drop(1).front != keyrange.front &&
645             check(keyrange.drop(1).front));
646 	assert(keyrange.drop(2).front != keyrange.front &&
647             keyrange.drop(2).front != keyrange.drop(1).front &&
648             check(keyrange.drop(2).front));
649 	assert(keyrange.drop(3).front == KeyValuePair("I", "You"));
650 	assert(keydbt.keyrange!(ubyte[], ubyte[]).walkLength == 4);
651 
652     keydbt.removeFront(2);
653     keydbt.insertFronts(tuple("Apple", "Pear"));
654 	assert(keyrange.drop(3).front == KeyValuePair("Apple", "Pear"));
655 	assert(keydbt.keyrange!(ubyte[], ubyte[]).walkLength == 4);
656 }
657 
658 struct RecnoBulkDbt
659 {
660 	DBT dbt = DBT(null, 0, 0, 0, 0, null, DB_DBT_BULK | DB_DBT_USERMEM);
661 	alias dbt this;
662 	uint32_t *pointer;
663 
664     invariant()
665     {
666         assert(dbt.flags & DB_DBT_BULK);
667     }
668 
669 	struct KeyRange(K, V)
670         if (isNumeric!(K)) {
671 		uint32_t *pointer;
672 		DBT *dbt;
673 
674 		struct KeyValuePair{
675 			K key;
676 			V value;
677 
678 			bool opEquals(KeyValuePair arg)
679 			{
680 				return key == arg.key && value == arg.value;
681 			}
682 
683 		};
684 
685 		alias KeyValuePair KV;
686 
687 		private this(RecnoBulkDbt* b) { 
688 			dbt = &b.dbt;
689 			pointer = cast(uint32_t*)(cast(ubyte*)dbt.data + 
690 					dbt.ulen) - 1;
691 		}
692 
693 		/// Forward range primitives.
694 		@property bool empty() const { 
695 			return !pointer || *pointer == 0; 
696 		}
697 		/// ditto
698 		@property KeyRange save() { return this; }
699 
700 		private @property Tuple!(K,V) _front(K, V)() 
701 		{ 
702 			uint32_t *__p = pointer;
703 			if (*__p == 0) return tuple(cast(K) 0, cast(V) null);
704 
705             uint32_t key = *__p--;
706 			ubyte *retdata = cast(uint8_t*)dbt.data + *__p--;
707 			uint32_t retdlen = *__p--;
708 
709 			return tuple(cast(K) key, frombytes!V(retdata[0..retdlen]));
710 		}
711 
712 		/// ditto
713 		@property KV front() 
714 		{ 
715 			KV res;
716 			auto v = _front!(K,V)();
717             res.key = v[0];
718             res.value = v[1];
719 
720 			return res; 
721 		}
722 
723 		/// ditto
724 		void popFront()
725 		{
726 			enforce(pointer);
727 			pointer -= 3;
728 		}
729 
730 		KV moveFront()
731 		{
732 			enforce(pointer);
733 			return front();
734 		}
735 	}
736 
737 	KeyRange!(K, V) keyrange(K, V)()
738 	{
739 		return KeyRange!(K, V)(&this);
740 	}
741 
742     void set_pointer()
743     {
744 		pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1;
745         while (*pointer != 0)
746         {
747             pointer -= 3;
748             if (pointer < data)
749             {
750                 throw new DbWrongUsingException("Constructing BulkDbt from wrong DBT");
751             }
752         }
753     }
754 
755     this(ref Dbt dbt)
756     {
757         if (!(dbt.flags & DB_DBT_BULK))
758         {
759             throw new DbWrongUsingException("Constructing RecnoBulkDbt from Dbt variable without DB_DBT_BULK in flags");
760         }
761         this.dbt = dbt;
762         set_pointer();
763     }
764 
765 	this(T)(int size, ref T arg)
766 	if (isForwardRange!T || is(typeof(arg.byKey())))
767 	in {
768 		assert(size > 0);
769 	}
770 	body {
771 		ulen = size;
772 		data = GC.malloc(size);
773 		pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1;
774 		*pointer = 0;
775 		insertFronts(arg);
776 	}
777 
778 	this()(int size)
779 	in {
780 		assert(size > 0);
781 	}
782 	body {
783 		ulen = size;
784 		data = GC.malloc(size);
785 		pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1;
786 		*pointer = 0;
787 	}
788 
789 	/*~this()
790 	{
791 		GC.free(data);
792 	}*/
793 
794 	@property bool empty() const
795 	{
796 		return pointer is null;
797 	}
798 
799 	/**
800 	  Duplicates the container. The elements themselves are not transitively
801 	  duplicated.
802 
803 	  Complexity: $(BIGOH n).
804 	 */
805 	/*TODO @property BulkDbt dup()
806 	{
807 		return BulkDbt(ulen, this[]);
808 	}*/
809 
810 	/**
811 	  Forward to $(D opSlice().front).
812 
813 	  Complexity: $(BIGOH 1)
814 	 */
815 	@property Tuple!(K, V) front(K,V)()
816     if (isNumeric!(K)) {
817 		uint32_t *__p = pointer;
818 		if (*__p == 0) return tuple(0, null);
819 
820         uint32_t key = *__p--;
821 		ubyte *retdata = cast(uint8_t*)dbt.data + *__p--;
822 		uint32_t retdlen = *__p--;
823 
824 		return tuple(key, retdata[0..retdlen]); 
825 	}
826 
827 	/**
828 	  Inserts $(D stuff) to the front of the container.
829 
830 	  Returns: The number of elements inserted
831 
832 	  Complexity: $(BIGOH log(n))
833 	 */
834 	size_t insertFront(K, V)(Tuple!(K, V) stuff)
835     if (isNumeric!(K)) {
836         uint32_t key = stuff[0];
837 		ubyte[] value = tobytes(stuff[1]);
838 
839 		uint32_t *__p = pointer;
840 		uint32_t __off = (pointer == 
841 		  (cast(uint32_t*)(cast(ubyte*)dbt.data + dbt.ulen) - 1)) 
842 			?  0 : __p[1] + __p[2];
843 		assert(__p && *__p == 0);
844 
845 		auto writedlen = value.length * typeof(value[0]).sizeof;
846 
847 		if ((cast(ubyte*)dbt.data + __off + writedlen) >
848 				cast(ubyte*)(__p - 3))
849 			throw new DbWrongUsingException("RecnoBulkDbt overflow");
850 		else {
851 			ubyte *writedata = cast(uint8_t*)dbt.data + __off;
852 			writedata[0..writedlen] = value[0..writedlen];
853 			__p[0] = key;
854 			__p[-1] = __off;
855 			__p[-2] = cast(uint32_t)(writedlen);
856 			__p[-3] = 0;
857 			pointer = __p - 3;
858 		}
859 
860 		return 1;
861 	}
862 
863 	/// ditto
864 	size_t insertFronts(Stuff)(Stuff stuff)
865 	if (is(typeof(stuff.byKey())))
866 	{
867 		size_t result;
868 		foreach (key; stuff.byKey())
869 		{
870 			insertFront(tuple(key, stuff[key]));
871 			++result;
872 		}
873 		return result;
874 	}
875 	
876 	/// ditto
877 	alias insertFront insert;
878 
879 	/// ditto
880 	alias insert stableInsert;
881 
882 	/// ditto
883 	alias insertFront stableInsertFront;
884 
885 	/**
886 	  Removes the value at the front of the container. 
887 
888 	  Precondition: $(D !empty)
889 
890 	  Complexity: $(BIGOH 1).
891 	 */
892 	void removeFront()
893 	{
894 		enforce(pointer);
895 		uint32_t *__p = pointer;
896 
897 		if ((cast(ubyte*)dbt.data + ulen) == cast(ubyte*)__p)
898 			throw new DbWrongUsingException("RecnoBulkDbt underflow");
899 
900 		__p[0] = 0;
901 		__p[1] = 0;
902 		__p[2] = 0;
903 		__p[3] = 0;
904 		pointer += 3;
905 	}
906 
907 	/// ditto
908 	alias removeFront stableRemoveFront;
909 
910 	/**
911 	  Removes $(D howMany) values at the front or back of the
912 	  container.
913 
914 Returns: The number of elements removed
915 
916 Complexity: $(BIGOH howMany * log(n)).
917      */
918 	size_t removeFront(size_t howMany)
919 	{
920 		size_t result;
921 		while (result < howMany)
922 		{
923 			removeFront();
924 			++result;
925 		}
926 		return result;
927 	}
928 
929 	/// ditto
930 	alias removeFront stableRemoveFront;
931 }
932 
933 unittest
934 {
935 	import std.stdio;
936 
937 	string[int] map;
938 	map[1] = "Hi!";
939 	map[2] = "Dog";
940 	map[100] = "White";
941 
942 	RecnoBulkDbt keydbt = RecnoBulkDbt(100, map);
943 
944 	auto keyrange = keydbt.keyrange!(int, string);
945 	
946 	alias typeof(keyrange).KeyValuePair KeyValuePair;
947 
948     /*Map doesn't guarantee any order*/
949     int check( typeof(keyrange.front) value )
950     {
951         return value == KeyValuePair(1, "Hi!") ||
952             value == KeyValuePair(2, "Dog") ||
953             value == KeyValuePair(100, "White");
954     }
955 
956 	assert(check(keyrange.front));
957 	assert(keyrange.drop(1).front != keyrange.front &&
958             check(keyrange.drop(1).front) );
959 	assert(keyrange.drop(2).front != keyrange.front &&
960 	        keyrange.drop(2).front != keyrange.drop(1).front &&
961             check(keyrange.drop(2).front));
962 	assert(keydbt.keyrange!(int, ubyte[]).walkLength == 3);
963 
964     keydbt.removeFront();
965     keydbt.insertFront(tuple(10, "Pear"));
966 	assert(keyrange.drop(2).front == KeyValuePair(10, "Pear"));
967 	assert(keydbt.keyrange!(int, ubyte[]).walkLength == 3);
968 }
969 
970 Dbt *toDbt(RecnoBulkDbt *dbt)
971 {
972     return cast(Dbt*) dbt;
973 }
974 
975 Dbt *toDbt(BulkDbt *dbt)
976 {
977     return cast(Dbt*) dbt;
978 }
979 
980 Dbt *toDbt(UserMemDbt *dbt)
981 {
982     return cast(Dbt*) dbt;
983 }