1 //// \file Document.d
2 
3 module tagion.betterC.hibon.Document;
4 
5 @nogc:
6 //import std.format;
7 import std.algorithm.iteration : map;
8 import std.algorithm.searching : count;
9 import std.conv : emplace;
10 import std.meta : AliasSeq, Filter;
11 import std.traits : EnumMembers, ForeachType, Unqual, getUDAs, isBasicType, isIntegral, isNumeric, isSomeString;
12 
13 //import core.stdc.stdio;
14 //import std.range.primitives : walkLength;
15 
16 import tagion.betterC.hibon.HiBON;
17 import tagion.betterC.utils.Bailout;
18 import tagion.betterC.utils.BinBuffer;
19 import tagion.betterC.utils.Memory;
20 import tagion.betterC.utils.Text;
21 import tagion.betterC.utils.sdt;
22 import LEB128 = tagion.betterC.utils.LEB128;
23 import tagion.betterC.hibon.BigNumber;
24 import tagion.betterC.hibon.HiBONBase;
25 
26 // import std.exception;
27 
28 static assert(uint.sizeof == 4);
29 
30 /**
31  * @brief Document is a lazy handler of HiBON serialized buffer
32  */
33 
34 /**
35  * Document is used for storage different data and provide
36  * possibility to read and analyze data
37  */
38 struct Document {
39     // @nogc:
40     /**
41      * HiBON Document value type
42      */
43     alias Value = ValueT!(false, void, Document);
44 
45     /**
46      * Actual data
47      */
48     protected immutable(ubyte)[] _data;
49 
50     /**
51      * Gets the internal buffer
52      * @return the buffer of the HiBON document
53      */
54     @nogc immutable(ubyte[]) data() const {
55         if (_data.length) {
56             ubyte[] result;
57             result.create(full_size);
58             foreach (i, elem; _data) {
59                 result[i] = elem;
60             }
61             return cast(immutable)(result);
62         }
63         ubyte[] empty_doc;
64         empty_doc.create(1);
65 
66         return cast(immutable)(empty_doc);
67     }
68 
69     /**
70       * Creates a HiBON Document from a buffer
71       * @param data - buffer
72       */
73     @nogc this(immutable(ubyte[]) data) pure {
74         this._data = data;
75     }
76 
77     /**
78      * Creates a replicate of a Document from another Document
79      * The buffer reused not copied
80      * @param doc - Document which is replicated
81      */
82     @nogc this(const Document doc) pure {
83         this._data = doc._data;
84     }
85 
86     /**
87      * Creates a document which is based on HiBON
88      * @param hibon - reference to the HiBON object
89      */
90     this(HiBONT hibon) {
91         //check hibon
92         this._data = hibon.serialize;
93     }
94 
95     this(const HiBONT hibon) {
96         //check hibon
97         auto mut_hibon = cast(HiBONT) hibon;
98         this._data = mut_hibon.serialize;
99     }
100 
101     /**
102      * Returns HiBON version
103      * @return HiBON version
104      */
105     uint ver() const {
106         if (data.length > ubyte.sizeof) {
107             if (data[ubyte.sizeof] == Type.VER) {
108                 const leb128_version = LEB128.decode!uint(data[ubyte.sizeof .. $]);
109                 return leb128_version.value;
110             }
111         }
112         return 0;
113     }
114 
115     void surrender() pure {
116         _data = null;
117     }
118 
119     /**
120      * Makes a cope od document
121      * @rapam doc - Document that will be copied
122      */
123     void copy(ref const Document doc) {
124         emplace(&this, doc);
125     }
126 
127     @property const {
128         @trusted bool empty() {
129             return data.length <= ubyte.sizeof;
130         }
131 
132         @nogc uint size() {
133             return LEB128.decode!uint(data).value;
134         }
135 
136         @nogc size_t full_size() {
137             if (_data.length) {
138                 const len = LEB128.decode!uint(_data);
139                 return len.size + len.value;
140             }
141             return 0;
142         }
143     }
144 
145     unittest { // Empty doc
146     {
147             Document doc;
148             assert(doc._data.length is 0);
149             assert(doc.data.length is 1);
150             assert(doc.empty);
151             assert(doc.size is 0);
152             assert(doc.length is 0);
153             auto range = doc[];
154             assert(range.empty);
155             range.popFront;
156             assert(range.empty);
157         }
158 
159         {
160             immutable(ubyte[]) _data = [0];
161             assert(_data.length is 1);
162             const doc = Document(_data);
163             assert(doc.data.length is 1);
164             assert(doc.empty);
165             assert(doc.size is 0);
166             assert(doc.length is 0);
167             assert(doc[].empty);
168         }
169     }
170 
171     // unittest { // Document with residual data
172     //     import tagion.betterC.hibon.HiBON;
173 
174     //     // import std.algorithm.comparison : equal;
175     //     auto h = HiBON();
176     //     h["test"] = 42;
177     //     immutable(ubyte[3]) residual = [42, 14, 217];
178     //     immutable data = h.serialize ~ residual;
179     //     const doc = Document(data);
180     //     assert(doc.full_size == h.serialize.length);
181     //     assert(doc.length == 1);
182     // }
183 
184     /**
185      * Counts the number of members in a Document
186      * @return number of members in in the Document
187      */
188     @nogc @property uint length() const {
189         uint count;
190         foreach (e; this[]) {
191             count++;
192         }
193         return count;
194     }
195 
196     /**
197      * The deligate used by the valid function to report errors
198      */
199     alias ErrorCallback = void delegate(scope const(Element) current,
200             scope const(Element) previous);
201 
202     /**
203      * Function check's if the Document is a valid HiBON format
204      * @param error_callback - if the delegate error_callback is the this function is call when a error occures
205      * @return error code of the validation
206      */
207     Element.ErrorCode valid(ErrorCallback error_callback = null) const {
208         auto previous = this[];
209         bool not_first;
210         foreach (ref e; this[]) {
211             Element.ErrorCode error_code;
212             Text work_key;
213             Text previous_work_key;
214             if (not_first && (key_compare(previous.front.key(previous_work_key), e.key(work_key)) >= 0)) {
215                 error_code = Element.ErrorCode.KEY_ORDER;
216             }
217             else if (e.type is Type.DOCUMENT) {
218                 error_code = e.get!(Document).valid(error_callback);
219             }
220             else {
221                 error_code = e.valid;
222             }
223             if (error_code !is Element.ErrorCode.NONE) {
224                 if (error_callback) {
225                     error_callback(e, previous.front);
226                 }
227                 return error_code;
228             }
229             if (not_first) {
230                 previous.popFront;
231             }
232             not_first = true;
233         }
234         return Element.ErrorCode.NONE;
235     }
236 
237     /**
238      * Check if a Document format is the correct HiBON format.
239      * Uses the valid function
240      * @return true if the Document is inorder
241      */
242     bool isInorder() const {
243         return valid() is Element.ErrorCode.NONE;
244     }
245 
246     /**
247      * Range of the Document
248      */
249     struct Range {
250     @nogc:
251         /**
252          * Buffer with data
253          */
254         immutable(ubyte)[] data;
255 
256         /**
257          * Version
258          */
259         immutable uint ver;
260     protected:
261         /**
262          * Range size
263          */
264         size_t _index;
265 
266         /**
267          * HiBON Element
268          */
269         Element _element;
270 
271     public:
272         @disable this();
273         /**
274          * Construct Range based on buffer
275          * @param data - buffer of data
276          */
277         this(immutable(ubyte[]) data) {
278             this.data = data;
279             if (data.length == 0) {
280                 _index = ubyte.sizeof;
281             }
282             else {
283                 _index = LEB128.calc_size(data);
284                 popFront();
285                 uint _ver;
286                 if (!empty && (front.type is Type.VER)) {
287                     const leb128_ver = LEB128.decode!uint(data[_index .. $]);
288                     _ver = leb128_ver.value;
289                     _index += leb128_ver.size;
290                 }
291                 ver = _ver;
292             }
293         }
294 
295         /**
296          * Construct Range based on other Document
297          * @param doc - Document
298          */
299         this(const Document doc) {
300             this(doc.data);
301         }
302 
303         @property pure const {
304             /**
305              * Checks is Range empty
306              * @return true if data length = 0
307              */
308             bool empty() {
309                 return data.length is 0;
310             }
311 
312             /**
313              * InputRange primitive operation
314              * @return currently iterated element
315              */
316         }
317         const(Element) front() {
318             return Element(data);
319         }
320 
321         /**
322          * InputRange primitive operation that advances the range to its next element.
323          */
324         void popFront() {
325             if (data.length) {
326                 data = data[Element(data).size .. $];
327             }
328         }
329     }
330 
331     /**
332      * @return range of Element's
333      */
334     @nogc Range opSlice() const {
335         if (full_size < _data.length) {
336             return Range(_data[0 .. full_size]);
337         }
338         return Range(_data);
339     }
340 
341     /**
342      * @return range of the member keys in the document
343      */
344     KeyRange keys() const {
345         return KeyRange(_data);
346     }
347 
348     protected struct KeyRange {
349     @nogc:
350         Text work_key;
351         Range range;
352         this(immutable(ubyte[]) data) {
353             range = Range(data);
354         }
355 
356         @property bool empty() const pure {
357             return range.empty;
358         }
359 
360         @property void popFront() {
361             range.popFront;
362         }
363 
364         string front() {
365             return range.front.key(work_key);
366         }
367 
368         ~this() {
369             work_key.dispose;
370         }
371     }
372 
373     /**
374      * The Document must only contain member names which represents an uint number
375      * Throws an std.conv.ConvException if the keys can not be convert to an uint
376      * @return range of indices of the type of uint in the Document
377     */
378     IndexRange indices() const {
379         return IndexRange(_data);
380     }
381 
382     protected struct IndexRange {
383     @nogc:
384         private {
385             Range range;
386             bool _error;
387         }
388         this(immutable(ubyte[]) data) {
389             range = Range(data);
390         }
391 
392         @property bool empty() const pure {
393             return range.empty;
394         }
395 
396         uint front() {
397             Text work_key;
398             const key = range.front.key(work_key);
399             uint index;
400             if (!is_index(key, index)) {
401                 _error = true;
402             }
403             return index;
404         }
405 
406         @property void popFront() {
407             range.popFront;
408         }
409 
410         @property error() const pure {
411             return _error;
412         }
413     }
414 
415     /**
416      * Check if the Document can be clasified as an Array
417      * @return true if all the keys in ordred numbers
418      */
419     bool isArray() const {
420         auto range = indices;
421         while (!range.empty) {
422             range.popFront;
423             if (range.error) {
424                 return false;
425             }
426         }
427         return true;
428     }
429 
430     /**
431      * @return true if the key exist in the Document
432      */
433     @trusted bool hasMember(scope string key) const {
434         return !opBinaryRight!("in")(key).isEod();
435     }
436 
437     /**
438      * @return true if the index exist in the Document
439      */
440     @trusted bool hasMember(Index)(scope Index index) const if (isIntegral!Index) {
441         return hasMember(index.to!string);
442     }
443 
444     /**
445      * Find the element with key
446      * @return the element with the key, if on element with this key has been found an empty element is returned
447      */
448     const(Element) opBinaryRight(string op)(in string key) const if (op == "in") {
449         foreach (element; this[]) {
450             Text work_key;
451             if (element.key(work_key).length == key.length) {
452                 bool isEqual = true;
453                 for (int i = 0; i < key.length; i++) {
454                     if (element.key(work_key)[i] != key[i]) {
455                         isEqual = false;
456                         break;
457                     }
458                 }
459                 if (isEqual) {
460                     return element;
461                 }
462             }
463             if (element.key(work_key) > key) {
464                 break;
465             }
466         }
467         return Element();
468     }
469 
470     const(Element) opBinaryRight(string op, Index)(const Index key) const
471     if ((op == "in") && (isIntegral!Index)) {
472         foreach (ref element; this[]) {
473             if (element.isIndex && (element.index == key)) {
474                 return element;
475             }
476             else if (element.key[0] > '9') {
477                 break;
478             }
479         }
480         return Element();
481     }
482 
483     /**
484      * @return the element with the key
485      * @throw if the element with the key is not found then and HiBONException is thrown
486      */
487     @trusted @nogc const(Element) opIndex(in string key) const {
488         auto result = key in this;
489         return result;
490     }
491 
492     /**
493      * @return the element with the index
494      * @throw if the element with the key is not found then and HiBONException is thrown
495        Or of the key is not an index a std.conv.ConvException is thrown
496      */
497     @trusted @nogc const(Element) opIndex(Index)(in Index index) const
498     if (isIntegral!Index) {
499         import tagion.betterC.utils.StringHelper;
500 
501         auto index_string = int_to_str(index);
502         scope (exit) {
503             index_string.dispose;
504         }
505 
506         return opIndex(index_string);
507     }
508 
509     /**
510      * Same as data
511      */
512     alias serialize = data;
513 
514     /**
515      * @param key, which size needs to be calculated
516      * @return the number of bytes taken up by the key in the HiBON serialized stream
517      */
518     @nogc static size_t sizeKey(const(char[]) key) pure {
519         uint index;
520         if (is_index(key, index)) {
521             return sizeKey(index);
522         }
523         return Type.sizeof + LEB128.calc_size(key.length) + key.length;
524     }
525 
526     /**
527      * @param key, which size needs to be calculated
528      * @return the number of bytes taken up by the key in the HiBON serialized stream
529      */
530     @nogc static size_t sizeKey(uint key) pure {
531         return Type.sizeof + ubyte.sizeof + LEB128.calc_size(key);
532     }
533 
534     unittest {
535         // Key is an index
536         assert(sizeKey("0") is 3);
537         assert(sizeKey("1000") is 4);
538         // Key is a labelw
539         assert(sizeKey("01000") is 7);
540     }
541 
542     /**
543      * Calculates the number of bytes taken up by an element in the HiBON serialized stream
544      * @param type = is the HIBON type
545      * @param key = is the key name
546      * @param x = is the value
547      * @return the number of bytes taken up by the element
548      */
549     @nogc static size_t sizeT(T, K)(Type type, K key, const(T) x) {
550         size_t size = sizeKey(key);
551         static if (is(T : U[], U)) {
552             const _size = x.length * U.sizeof;
553             size += LEB128.calc_size(_size) + _size;
554         }
555         else static if (is(T : const Document)) {
556             size += calc_size(x.data.length) + x.data.length;
557         }
558         else static if (is(T : const BigNumber)) {
559             size += x.calc_size;
560         }
561         else static if (isDataBlock!T) {
562             const _size = x.size;
563             size += LEB128.calc_size(_size) + _size;
564         }
565         else {
566             //alias BaseT=TypedefType!T;
567             static if (isIntegral!T) {
568                 size += LEB128.calc_size(x);
569             }
570             else {
571                 size += T.sizeof;
572             }
573         }
574         return size;
575     }
576 
577     /**
578      * Append the key to the buffer
579      * @param buffer = is the target buffer
580      * @param type = is the HiBON type
581      * @param key = is the member key
582      * @param index = is offset index in side the buffer and index with be progressed
583      */
584     static void buildKey(K)(
585             ref BinBuffer buffer, Type type, const K key) if (is(K : const(char[])) || is(K == uint)) {
586         static if (is(K : const(char[]))) {
587             uint key_index;
588             if (is_index(key, key_index)) {
589                 buildKey(buffer, type, key_index);
590                 return;
591             }
592         }
593         buffer.write(type);
594         static if (is(K : const(char[]))) {
595             LEB128.encode(buffer, key.length);
596             buffer.write(key);
597         }
598         else {
599             buffer.write(ubyte(0));
600             LEB128.encode(buffer, key);
601         }
602     }
603 
604     /**
605      * Append a full element to a buffer
606      * @param buffer = is the target buffer
607      * @param type = is the HiBON type
608      * @param key = is the member key
609      * @param x = is the value of the element
610      * @param index = is offset index in side the buffer and index with be progressed
611      */
612     static void build(T, K)(ref BinBuffer buffer, Type type, const K key, const(T) x)
613             if (is(K : const(char[])) || is(K == uint)) {
614         const build_size = buffer.length;
615         buildKey(buffer, type, key);
616         static if (is(T : U[], U)) {
617             immutable size = cast(uint)(x.length * U.sizeof);
618             LEB128.encode(buffer, size);
619             buffer.write(x);
620         }
621         else static if (is(T : const Document)) {
622             buffer.write(x.data);
623         }
624         else static if (is(T : const BigNumber)) {
625             buffer.write(x.serialize);
626         }
627         else static if (is(T : const DataBlock)) {
628             x.serialize(buffer);
629         }
630         else static if (is(T : const sdt_t)) {
631             LEB128.encode(buffer, x.time);
632         }
633         else static if (isIntegral!T) {
634             LEB128.encode(buffer, x);
635         }
636         else {
637             buffer.write(x);
638         }
639     }
640 
641     /**
642      * This range is used to generate and range of same type U
643      * If the Document contains and Array of the elements this range can be used
644      * @param Range (Array) of the type U
645      */
646     RangeT!U range(T : U[], U)() const {
647         return RangeT!U(data);
648     }
649 
650     struct RangeT(T) {
651     @nogc:
652         Range range;
653         enum EType = Value.asType!T;
654         static assert(EType !is Type.NONE, format("Range type %s not supported", T.stringof));
655         this(immutable(ubyte)[] data) {
656             range = Range(data);
657         }
658 
659         @property {
660             immutable(ubyte[]) data() {
661                 return range.data;
662             }
663 
664             void popFront() {
665                 range.popFront;
666             }
667 
668             const(T) front() const {
669                 return range.front.get!T;
670             }
671 
672             uint index() const {
673                 return range.front.index;
674             }
675 
676             const {
677                 bool empty() pure {
678                     return range.empty;
679                 }
680 
681                 string key(ref Text work_key) {
682                     return range.front.key(work_key);
683                 }
684             }
685         }
686     }
687 
688     version (unittest) {
689         import std.typecons : Tuple, isTuple;
690         import tagion.betterC.utils.Basic : basename;
691 
692         static private void make(R)(ref ubyte[] buffer, R range, size_t count = size_t.max) if (isTuple!R) {
693             size_t temp_index;
694             auto temp_buffer = buffer.dup;
695             foreach (i, t; range) {
696                 if (i is count) {
697                     break;
698                 }
699                 enum name = range.fieldNames[i];
700                 alias U = range.Types[i];
701                 enum E = Value.asType!U;
702                 static if (name.length is 0) {
703                     build(temp_buffer, E, cast(uint) i, t, temp_index);
704                 }
705                 else {
706                     build(temp_buffer, E, name, t, temp_index);
707                 }
708             }
709             auto leb128_size_buffer = LEB128.encode(temp_index);
710             size_t index;
711             buffer.array_write(leb128_size_buffer, index);
712             buffer.array_write(temp_buffer[0 .. temp_index], index);
713         }
714     }
715 
716     unittest {
717         { // Test of null document
718             const doc = Document(null);
719             assert(doc.length is 0);
720             assert(doc[].empty);
721         }
722 
723         { // Test of empty Document
724             auto buffer = BinBuffer(0x200);
725             size_t index;
726             buffer.write(uint.init);
727             buffer.write(Type.NONE);
728             buffer.write(uint(1), 0);
729             const doc_buffer = buffer[0 .. index];
730             const doc = Document(doc_buffer.serialize);
731             assert(doc.length is 0);
732             assert(doc[].empty);
733 
734         }
735     }
736 
737     /**
738     * HiBON Element representation
739     */
740     struct Element {
741     @nogc:
742         /*
743          * -----
744          * //data image:
745          * +-------------------------------------------+
746          * | [Type] | [len] | [key] | [val | unused... |
747          * +-------------------------------------------+
748          *          ^ type offset(1)
749          *                  ^ len(sizeKey)
750          *                          ^ sizeKey + 1 + len(sizeKey)
751          *                                 ^ size
752          *                                             ^ data.length
753          *
754          */
755         immutable(ubyte[]) data;
756     public:
757         /**
758          * Construct an Element based on buffer
759          * @param data - buffer
760          */
761         this(immutable(ubyte[]) data) {
762             // In this time, Element does not parse a binary data.
763             // This is lazy initialization for some efficient.
764             this.data = data;
765         }
766 
767         //enum KEY_POS = Type.sizeof + keyLen.sizeof;
768 
769         /**
770          * Evaluates key position
771          * @return key position
772          */
773         @property uint keyPos() const {
774             if (isIndex) {
775                 return Type.sizeof + ubyte.sizeof;
776             }
777             return cast(uint)(Type.sizeof + LEB128.calc_size(data[Type.sizeof .. $]));
778         }
779 
780         @property const {
781             /**
782              * @return true if the elemnt is of T
783              */
784             bool isType(T)() {
785                 enum E = Value.asType!T;
786                 return (E !is Type.NONE) && (type is E);
787             }
788 
789             /**
790              * @return the HiBON Value of the element
791              * @throw if  the type is invalid and HiBONException is thrown
792              */
793             const(Value) value() {
794                 immutable value_pos = valuePos;
795                 with (Type)
796             TypeCase : switch (type) {
797                     static foreach (E; EnumMembers!Type) {
798                         static if (isHiBONBaseType(E)) {
799                 case E:
800                             static if (E is DOCUMENT) {
801                                 immutable len = LEB128.decode!uint(data[value_pos .. $]);
802                                 return Value(Document(
803                                         data[value_pos .. value_pos + len.size + len.value]));
804                             }
805                             else static if ((E is STRING) || (E is BINARY)) {
806                                 alias T = Value.TypeT!E;
807                                 alias U = ForeachType!T;
808                                 immutable binary_len = LEB128.decode!uint(data[value_pos .. $]);
809                                 immutable buffer_pos = value_pos + binary_len.size;
810                                 immutable buffer = (cast(immutable(U)*)(data[buffer_pos .. $].ptr))[0 .. binary_len
811                                     .value];
812                                 return Value(buffer);
813                             }
814                             else static if (E is BIGINT) {
815                                 return Value(BigNumber(data[value_pos .. $]));
816                             }
817                             else static if (isDataBlock(E)) {
818                                 // immutable binary_len=LEB128.decode!uint(data[value_pos..$]);
819                                 // immutable buffer_pos=value_pos+binary_len.size;
820                                 // immutable buffer=data[buffer_pos..buffer_pos+binary_len.value];
821                                 return Value(DataBlock(data[value_pos .. $]));
822                             }
823                             else {
824                                 if (isHiBONBaseType(type)) {
825                                     static if (E is TIME) {
826                                         alias T = long;
827                                     }
828                                     else {
829                                         alias T = Value.TypeT!E;
830                                     }
831                                     static if (isIntegral!T) {
832                                         return Value(LEB128.decode!T(data[value_pos .. $]).value);
833                                     }
834                                     else {
835                                         Value* result = cast(Value*)(&data[value_pos]);
836                                         return *result;
837                                     }
838                                 }
839                             }
840                             break TypeCase;
841                         }
842                     }
843                 default:
844                     //empty
845                 }
846                 return Value.init;
847                 //                assert(0);
848             }
849 
850             /**
851              * @return the value as the HiBON type Type
852              * @throw if the element does not contain the type E and HiBONException is thrown
853              */
854             auto by(Type E)() const {
855                 return value.by!E;
856             }
857 
858             /**
859              * @return the value as the type T
860              * @throw if the element does not contain the type and HiBONException is thrown
861              */
862             @trusted const(T) get(T)() const {
863                 enum E = Value.asType!T;
864                 static assert(E !is Type.NONE, "Unsupported type " ~ T.stringof);
865                 return by!E;
866             }
867 
868             /**
869              * Tryes to convert the value to the type T.
870              * @return true if the function succeeds
871              */
872             bool as(T)(ref T result) {
873                 switch (type) {
874                     static foreach (E; EnumMembers!Type) {
875                         static if (isHiBONBaseType(E)) {
876                 case E:
877                             alias BaseT = Value.TypeT!E;
878                             static if (isImplicitlyConvertible!(BaseT, T)) {
879                                 result = value.get!BaseT;
880                                 return true;
881                             }
882                             else static if (__traits(compiles, value.get!(BaseT).to!T)) {
883                                 result = value.get!(BaseT)
884                                     .to!T;
885                             }
886                         }
887                     }
888                 }
889                 return false;
890             }
891 
892             /**
893              * @return the index of the key
894              * @throw if the key is not an index an HiBONException is thrown
895              */
896             uint index() {
897                 return LEB128.decode!uint(data[keyPos .. $]).value;
898             }
899 
900         }
901 
902         @property const pure {
903             /**
904              * @return true if the buffer block ends
905              */
906             bool isEod() {
907                 return data.length == 0;
908             }
909 
910             /**
911              * @return the Type of the element
912              */
913             Type type() {
914                 if (isEod) {
915                     return Type.NONE;
916                 }
917                 return cast(Type)(data[0]);
918             }
919 
920             /**
921              * @return true if element key is an index
922              */
923         }
924         bool isIndex() const {
925             return data[Type.sizeof] is 0;
926         }
927 
928         @property const {
929             /**
930              * @return the key length
931              */
932             uint keyLen() {
933                 if (isIndex) {
934                     return cast(uint) LEB128.calc_size(data[keyPos .. $]);
935                 }
936                 return LEB128.decode!uint(data[Type.sizeof .. $]).value;
937             }
938 
939             /**
940              * @return the key
941              */
942             string key(ref Text key_index) {
943                 if (isIndex) {
944                     key_index(LEB128.decode!uint(data[keyPos .. $]).value);
945                     return key_index.serialize;
946                 }
947                 return cast(string) data[keyPos .. valuePos];
948             }
949 
950             /**
951              * @return the position of the value inside the element buffer
952              */
953             uint valuePos() {
954                 return keyPos + keyLen;
955             }
956 
957             uint dataPos() {
958                 return valuePos + cast(uint) LEB128.calc_size(data[valuePos .. $]);
959             }
960 
961             uint dataSize() {
962                 return LEB128.decode!uint(data[valuePos .. $]).value;
963             }
964             /**
965              * @return the size of the element in bytes
966              */
967             size_t size() {
968                 with (Type) {
969                 TypeCase:
970                     switch (type) {
971                         static foreach (E; EnumMembers!Type) {
972                     case E:
973                             static if (isHiBONBaseType(E)) {
974                                 alias T = Value.TypeT!E;
975                                 static if (
976                                     (E is STRING) || (E is DOCUMENT) ||
977                                         (E is BINARY)) {
978                                     return dataPos + dataSize;
979                                 }
980                                 else static if (isDataBlock(E)) {
981                                     return dataPos + dataSize;
982                                 }
983                                 else static if (E is BIGINT) {
984                                     return valuePos + LEB128.calc_size(data[valuePos .. $]);
985                                 }
986                                 else {
987                                     static if (E is TIME) {
988                                         alias BaseT = long;
989                                     }
990                                     else {
991                                         alias BaseT = T;
992                                     }
993                                     static if (isIntegral!BaseT) {
994                                         return valuePos + LEB128.calc_size(data[valuePos .. $]);
995                                     }
996                                     else {
997                                         return valuePos + BaseT.sizeof;
998                                     }
999                                 }
1000                             }
1001                             else static if (isNative(E)) {
1002                                 static if (E is NATIVE_DOCUMENT) {
1003                                     const doc = Document(data[valuePos .. $]);
1004                                     return valuePos + dataSize + doc.size;
1005                                 }
1006                             }
1007                             else static if (E is Type.NONE) {
1008                                 goto default;
1009                             }
1010                             break TypeCase;
1011                         }
1012                     default:
1013                         return LEB128.calc_size(data);
1014                     }
1015                 }
1016                 return 0;
1017                 // import std.format;
1018                 // assert(0, format("Bad type %s", type));
1019                 // Text error;
1020                 // error("Bad type")(type);
1021                 // assert(0, error.serialize);
1022             }
1023 
1024             enum ErrorCode {
1025                 NONE, // No errors
1026                 INVALID_NULL, // Invalid null object
1027                 KEY_ORDER, // Error in the key order
1028                 DOCUMENT_TYPE, // Warning document type
1029                 TOO_SMALL, // Data stream is too small to contain valid data
1030                 ILLEGAL_TYPE, // Use of internal types is illegal
1031                 INVALID_TYPE, // Type is not defined
1032                 OVERFLOW, // The specifed data does not fit into the data stream
1033                 ARRAY_SIZE_BAD // The binary-array size in bytes is not a multipla of element size in the array
1034             }
1035 
1036             /**
1037              * Check if the element is valid
1038              * @return the error code the element.
1039                ErrorCode.NONE means that the element is valid
1040             */
1041             @trusted ErrorCode valid() {
1042                 enum MIN_ELEMENT_SIZE = Type.sizeof + ubyte.sizeof + char.sizeof + ubyte.sizeof;
1043 
1044                 with (ErrorCode) {
1045                     if (type is Type.DOCUMENT) {
1046                         return DOCUMENT_TYPE;
1047                     }
1048                     if (data.length < MIN_ELEMENT_SIZE) {
1049                         if (data.length !is ubyte.sizeof) {
1050                             return TOO_SMALL;
1051                         }
1052                         else if (data[0]!is 0) {
1053                             return INVALID_NULL;
1054                         }
1055                     }
1056                 TypeCase:
1057                     switch (type) {
1058                         static foreach (E; EnumMembers!Type) {
1059                     case E:
1060                             static if ((isNative(E) || (E is Type.DEFINED_ARRAY))) {
1061                                 return ILLEGAL_TYPE;
1062                             }
1063                             break TypeCase;
1064                         }
1065                     default:
1066                         return INVALID_TYPE;
1067                     }
1068                     if (size > data.length) {
1069                         return OVERFLOW;
1070                     }
1071                     if (type is Type.BINARY) {
1072                         const leb128_size = LEB128.decode!ulong(data[valuePos .. $]);
1073                         if (leb128_size.value > uint.max) {
1074                             return OVERFLOW;
1075                         }
1076                     }
1077                     return NONE;
1078                 }
1079             }
1080         }
1081 
1082         @property const pure {
1083             /**
1084              * Check if the type match That template.
1085              * That template must have one parameter T as followes
1086              * @return true if the element is the type That
1087             */
1088             bool isThat(alias That)() {
1089             TypeCase:
1090                 switch (type) {
1091                     static foreach (E; EnumMembers!Type) {
1092                 case E:
1093                         static if (isHiBONBaseType(E)) {
1094                             alias T = Value.TypeT!E;
1095                             return That!T;
1096                         }
1097                         break TypeCase;
1098                     }
1099                 default:
1100                     // empty
1101                 }
1102                 return false;
1103             }
1104         }
1105         /**
1106          * Compare two elements
1107          */
1108         bool opEquals(ref const Element other) const {
1109             immutable s = size;
1110             if (s !is other.size) {
1111                 return false;
1112             }
1113             return data[0 .. s] == other.data[0 .. s];
1114         }
1115     }
1116 }