1 /// \file HiBON.d
2 
3 /**
4  * @brief Implements HiBON
5  * Hash-invariant Binary Object Notation
6  * Is inspired by BSON but us not compatible
7  *
8  * @see
9  * <a href="http://bsonspec.org/">BSON - Binary JSON</a> 
10  */
11 module tagion.betterC.hibon.HiBON;
12 
13 @nogc:
14 //import std.container : RedBlackTree;
15 //import std.format;
16 import std.meta : staticIndexOf;
17 
18 //import std.algorithm.iteration : map, fold, each;
19 import std.traits : EnumMembers, ForeachType, Unqual, isMutable, isBasicType, PointerTarget,
20     isAssociativeArray;
21 import std.meta : AliasSeq;
22 import std.range : enumerate, isInputRange;
23 
24 //import std.conv : to;
25 //import std.typecons : TypedefType;
26 
27 import tagion.betterC.hibon.BigNumber;
28 import tagion.betterC.hibon.Document;
29 import tagion.betterC.hibon.HiBONBase;
30 import tagion.betterC.utils.Bailout;
31 import tagion.betterC.utils.Basic;
32 import tagion.betterC.utils.BinBuffer;
33 import tagion.betterC.utils.Memory;
34 import tagion.betterC.utils.RBTree;
35 import tagion.betterC.utils.Text;
36 import LEB128 = tagion.betterC.utils.LEB128;
37 
38 import tagion.betterC.utils.platform;
39 
40 //import core.stdc.stdio;
41 
42 import std.stdio;
43 
44 HiBONT HiBON() {
45     HiBONT result = HiBONT(RBTree!(HiBONT.Member*)(), true, false);
46     return result;
47 }
48 
49 /**
50  * HiBON is a generate obje52ct of the HiBON format
51  */
52 struct HiBONT {
53 @nogc:
54     /**
55      * Gets the internal buffer
56      * @return the buffer of the HiBON document
57      */
58     alias Members = RBTreeT!(Member*);
59 
60     //     RedBlackTree!(Member, (a, b) => (less_than(a.key, b.key)));
61     private {
62         Members _members;
63         bool _owns; /// show is it owning
64         bool _readonly; /// true if read only
65         bool _self_destruct; /// true if self-destruced
66         BinBuffer _buffer;
67     }
68 
69     uint error;
70 
71     alias Value = ValueT!(true, HiBONT*, Document);
72 
73     /**
74      * Destructor
75      */
76     ~this() {
77         dispose;
78     }
79 
80     void dispose() {
81         if (_owns) {
82             _members.dispose;
83         }
84         else {
85             _members.surrender;
86         }
87         _buffer.dispose;
88         if (_self_destruct) {
89             HiBONT* self = &this;
90 
91             
92 
93             .dispose!false(self);
94         }
95     }
96 
97     invariant {
98         assert(_owns || (!_owns && _readonly));
99     }
100 
101     /**
102      * Calculate the size in bytes of HiBON payload
103      * @return the size in bytes
104      */
105     size_t size() const {
106         size_t result;
107         foreach (n; _members[]) {
108             result += n.size;
109         }
110         if (result > 0) {
111             return result; //+calc_size(result);
112         }
113         else {
114             return ubyte.sizeof;
115         }
116     }
117 
118     /**
119      * Calculated the size in bytes of serialized HiBON
120      * @return the size in bytes
121      */
122     size_t serialize_size() const {
123         auto _size = size;
124         if (_size !is ubyte.sizeof) {
125             _size += LEB128.calc_size(_size);
126         }
127         return _size;
128     }
129 
130     /**
131      * Expropriate the members to the return
132      * @return The new owner of the members
133      */
134     HiBONT* expropriate() {
135         auto result = create!(HiBONT);
136         // Surrender the RBTree to the result;
137         result._members = _members.expropriate;
138         result._owns = true;
139         result._self_destruct = true;
140         _readonly = true;
141         _owns = false;
142 
143         //        expropriate;
144         return result;
145     }
146 
147     @property bool readonly() const pure {
148         return _readonly;
149     }
150 
151     @property bool owns() const pure {
152         return _owns;
153     }
154 
155     /**
156      * Generated the serialized HiBON
157      * @return the byte stream
158      */
159     immutable(ubyte[]) serialize() {
160         _buffer.recreate(serialize_size);
161         append(_buffer);
162         return _buffer.serialize;
163     }
164 
165     // /**
166     //  Helper function to append
167     //  */
168     private void append(ref BinBuffer buffer) const {
169         if (_members.empty) {
170             buffer.write(ubyte(0));
171         }
172         else {
173             uint size;
174             foreach (m; _members[]) {
175                 size += m.size;
176             }
177             LEB128.encode(buffer, size);
178             foreach (n; _members[]) {
179                 n.append(buffer);
180             }
181         }
182     }
183 
184     /**
185      ** Internal Member in the HiBON class
186      */
187     struct Member {
188     @nogc:
189         private {
190             char[] _key;
191             Type _type;
192             Value _value;
193         }
194 
195         int opCmp(ref const(Member*) b) const pure {
196             return opCmp(b._key);
197         }
198 
199         int opCmp(const(char[]) key) const pure {
200             int res = 1;
201             if (this._key.length == key.length) {
202                 res = 0;
203                 foreach (i, elem; key) {
204                     if (this._key[i] != elem) {
205                         res = 1;
206                     }
207                 }
208             }
209             else if (this._key < key) {
210                 res = -1;
211             }
212             return res;
213         }
214 
215         bool opEquals(T)(T b) const pure {
216             return opCmp(b) == 0;
217         }
218 
219         alias CastTypes = AliasSeq!(uint, int, ulong, long, string);
220         /**
221          * Construct a Member
222          * @param x = the parameter value
223          * @param key = the name of the member
224          */
225         this(T)(T x, in const(char[]) key) {
226 
227             
228 
229                 .create(this._key, key);
230             void _init(S)(S x) {
231                 enum E = Value.asType!S;
232                 this._type = E;
233                 static if (.isArray(E)) {
234                     alias U = Unqual!(ForeachType!S);
235                     U[] temp_x;
236                     temp_x.create(x);
237                     this._value = cast(S) temp_x;
238                 }
239                 else static if (E is Type.BIGINT) {
240                     this._value = x;
241                 }
242                 else {
243                     this._value = cast(UnqualT) x;
244                 }
245 
246             }
247 
248             alias UnqualT = Unqual!T;
249             enum E = Value.asType!UnqualT;
250             static if (E is Type.NONE) {
251                 alias CastT = CastTo!(UnqualT, CastTypes);
252                 static assert(!is(CastT == void), "Type " ~ T.stringof ~ " is not valid");
253                 _init(x);
254             }
255             else {
256                 _init(x);
257             }
258 
259         }
260 
261         private this(in const(char[]) key) {
262             _key = Text(key).expropriate;
263         }
264 
265         private this(in size_t index) {
266             _key = Text(index).expropriate;
267         }
268 
269         static Member* create(T)(T x, in const(char[]) key) {
270             // auto new_member=Member(x, key);
271             // scope(exit) {
272             //     new_member._key=null;
273             // }
274             auto result = .create!Member(x, key);
275             // result._key=new_nember._key;
276             // result._type=new_member._type;
277             // result._value=new_member._value;
278             return result;
279         }
280 
281         @property const pure {
282             Type type() {
283                 return _type;
284             }
285 
286             string key() {
287                 return cast(immutable) _key;
288             }
289 
290             Value value() {
291                 return _value;
292             }
293 
294             size_t key_size() {
295                 uint index;
296                 if (is_index(_key, index)) {
297                     return ubyte.sizeof + LEB128.calc_size(index);
298                 }
299                 return LEB128.calc_size(_key.length) + _key.length;
300             }
301         }
302 
303         ~this() {
304             dispose;
305         }
306 
307         void dispose() {
308             with (Type) {
309             TypeCase:
310                 final switch (type) {
311                     static foreach (E; EnumMembers!Type) {
312                 case E:
313                         static if (.isArray(E)) {
314                             alias U = Unqual!(ForeachType!(Value.TypeT!E));
315                             auto remove_this = cast(U[]) value.by!E;
316 
317                             
318 
319                             .dispose(remove_this);
320                         }
321                         else static if (E is Type.DOCUMENT) {
322                             alias T = Unqual!(PointerTarget!(Value.TypeT!E));
323                             auto sub = value.by!(E);
324                             auto remove_this = cast(T*)(value.by!(E));
325                             remove_this.dispose;
326                         }
327                         break TypeCase;
328                     }
329                 }
330             }
331             _key.dispose;
332         }
333 
334         /**
335          * If the value of the Member contains a Document it returns it or else an error is asserted
336          * @return the value as a Document
337          */
338         const(HiBONT*) document() const pure
339         in {
340             assert(type is Type.DOCUMENT);
341         }
342         do {
343             return value.document;
344         }
345 
346         /**
347          * @return the value as type T
348          * @throw if the member does not match the type T and HiBONException is thrown
349          */
350         const(T) get(T)() const {
351             enum E = Value.asType!T;
352 
353             
354 
355             .check(E is type, message("Expected HiBON type %s but apply type %s (%s)", type, E, T
356                     .stringof));
357             return value.by!E;
358         }
359 
360         /**
361          * @return the value as HiBON Type E
362          * @throw if the member does not match the type T and HiBONException is thrown
363          */
364         auto by(Type type)() inout {
365             return value.by!type;
366         }
367 
368         /**
369          * Calculates the size in bytes of the Member
370          * @return the size in bytes
371          */
372         size_t size() const {
373             with (Type) {
374             TypeCase:
375                 switch (type) {
376                     foreach (E; EnumMembers!Type) {
377                         static if (isHiBONBaseType(E) || isNative(E)) {
378                 case E:
379                             static if (E is Type.DOCUMENT) {
380                                 const _size = value.by!(E).size;
381                                 if (_size is 1) {
382                                     return Document.sizeKey(key) + ubyte.sizeof;
383                                 }
384                                 return Document.sizeKey(key) + LEB128.calc_size(_size) + _size;
385                             }
386                             else static if (E is NATIVE_DOCUMENT) {
387                                 const _size = value.by!(E).size;
388                                 return Document.sizeKey(key) + LEB128.calc_size(_size) + _size;
389                             }
390                             else static if (E is VER) {
391                                 return LEB128.calc_size(HIBON_VERSION);
392                             }
393                             else {
394                                 const v = value.by!(E);
395                                 return Document.sizeT(E, key, v);
396                             }
397                             break TypeCase;
398                         }
399                     }
400                 default:
401                     // Empty
402                 }
403                 assert(0, "Size of HiBON type %s is not valid");
404             }
405         }
406 
407         protected void appendList(Type E)(ref BinBuffer buffer) const
408         if (isNativeArray(E)) {
409             immutable size_index = buffer.length;
410             buffer.write(uint.init);
411             scope (exit) {
412                 buffer.write(Type.NONE);
413                 immutable doc_size = cast(uint)(buffer.length - size_index - uint.sizeof);
414                 buffer.write(doc_size);
415             }
416             with (Type) {
417                 foreach (i, h; value.by!E) {
418                     const key = Text()(i);
419                     //immutable key=i.to!string;
420                     static if (E is NATIVE_STRING_ARRAY) {
421                         Document.build(buffer, STRING, key.serialize, h);
422                     }
423                     else {
424                         Document.buildKey(buffer, DOCUMENT, key.serialize);
425                         static if (E is NATIVE_HIBON_ARRAY) {
426                             h.append(buffer);
427                         }
428                         else static if (E is NATIVE_DOCUMENT_ARRAY) {
429                             buffer.write(h.data);
430                         }
431 
432                         else {
433                             assert(0, "Support is not implemented yet");
434                         }
435                     }
436                 }
437             }
438 
439         }
440 
441         void append(ref BinBuffer buffer) const {
442             with (Type) {
443             TypeCase:
444                 switch (type) {
445                     static foreach (E; EnumMembers!Type) {
446                         static if (isHiBONBaseType(E) || isNative(E)) {
447                 case E:
448                             alias T = Value.TypeT!E;
449                             static if (E is DOCUMENT) {
450                                 Document.buildKey(buffer, E, key);
451                                 value.by!(E).append(buffer);
452                             }
453                             else static if (isNative(E)) {
454                                 static if (E is NATIVE_DOCUMENT) {
455                                     Document.buildKey(buffer, DOCUMENT, key);
456                                     const doc = value.by!(E);
457                                     buffer.write(value.by!(E).data);
458                                 }
459                                 else {
460                                     goto default;
461                                 }
462                             }
463                             else {
464                                 alias U = typeof(value.by!E());
465                                 static if (is(U == const(float))) {
466                                     auto x = value.by!E;
467                                 }
468                                 Document.build(buffer, E, key, value.by!E);
469                             }
470                             break TypeCase;
471                         }
472                     }
473                 default:
474                     assert(0, "Illegal type");
475                 }
476             }
477         }
478     }
479 
480     /**
481      * @return Range of members
482      */
483     Members.Range opSlice() const {
484         return _members[];
485     }
486 
487     void opIndexAssign(ref HiBONT x, in const(char[]) key) {
488         if (!readonly && is_key_valid(key)) {
489             auto new_x = x.expropriate;
490             // auto new_member=Member(new_x, key);
491             // scope(exit) {
492             //     new_member._key=null;
493             // }
494             // char[] new_key;
495             // create(new_key, key);
496             auto new_member = Member.create(new_x, key);
497             _members.insert(new_member);
498         }
499         else {
500             error++;
501         }
502     }
503 
504     void opAssign(T)(T r) if ((isInputRange!T) && !isAssociativeArray!T) {
505         foreach (i, a; r.enumerate) {
506             opIndexAssign(a, i);
507         }
508     }
509 
510     /**
511      * Assign and member x with the key
512      * @param x = parameter value
513      * @param key = member key
514      */
515     void opIndexAssign(T)(T x, in const(char[]) key) if (!is(T : const(HiBONT))) {
516         if (!readonly && is_key_valid(key)) {
517             auto new_member = create!Member(x, key);
518             _members.insert(new_member);
519         }
520         else {
521             error++;
522         }
523     }
524 
525     /**
526      * Assign and member x with the index
527      * @param x = parameter value
528      * @paramindex = member index
529      */
530     void opIndexAssign(T)(T x, const size_t index) if (!is(T : const(HiBONT))) {
531         import tagion.betterC.utils.StringHelper;
532 
533         if (index <= uint.max) {
534             const _key = int_to_str(index);
535             opIndexAssign(x, _key);
536         }
537         else {
538             error++;
539         }
540     }
541 
542     void opIndexAssign(ref HiBONT x, const size_t index) {
543         if (index <= uint.max) {
544             Text key_text;
545             key_text(index);
546             //auto _key=Key(cast(uint)index);
547             this[key_text.serialize] = x;
548         }
549         else {
550             error++;
551         }
552     }
553 
554     /**
555      * Access an member at key
556      * @param key = member key
557      * @return the Member at the key
558      * @throw if the an member with the key does not exist an HiBONException is thrown
559      */
560     const(Member*) opIndex(in const(char[]) key) const {
561         auto m = Member(key);
562         return _members.get(&m);
563     }
564 
565     /**
566      * Access an member at index
567      * @param index = member index
568      * @return the Member at the index
569      * @throw if the an member with the index does not exist an HiBONException is thrown
570      Or an std.conv.ConvException is thrown if the key is not an index
571      */
572     const(Member*) opIndex(const size_t index) {
573         if (index <= uint.max) {
574             auto m = Member(index);
575             return _members.get(&m);
576         }
577         error++;
578         return null;
579     }
580 
581     /**
582      * @param key = member key
583      * @return true if the member with the key exists
584      */
585     bool hasMember(in const(char[]) key) const {
586         auto m = Member(key);
587         return _members.exists(&m);
588     }
589 
590     /**
591      * Removes a member with name of key
592      * @param key = name of the member to be removed
593      */
594     void remove(in const(char[]) key) {
595         auto m = Member(key);
596         _members.remove(&m);
597     }
598 
599     ///
600     unittest { // remove
601         auto hibon = HiBON();
602         hibon["d"] = 4;
603         hibon["b"] = 2;
604         hibon["c"] = 3;
605         hibon["a"] = 1;
606 
607         assert(hibon.hasMember("b"));
608         hibon.remove("b");
609         assert(!hibon.hasMember("b"));
610     }
611 
612     /**
613      * @return the number of members in the HiBON
614      */
615     size_t length() const {
616         return _members.length;
617     }
618 
619     /**
620      * @return a list of the member keys
621      */
622     KeyRange keys() const {
623         return KeyRange(&this);
624     }
625 
626     protected struct KeyRange {
627     @nogc:
628         Members.Range range;
629         this(const(HiBONT*) owner) {
630             range = owner.opSlice;
631         }
632 
633         ~this() {
634             range.dispose;
635         }
636 
637         @property bool empty() const pure {
638             return range.empty;
639         }
640 
641         @property void popFront() {
642             range.popFront;
643         }
644 
645         string front() {
646             return range.front.key;
647         }
648     }
649 
650     /**
651      * A list of indices
652      * @return returns false if some index is not a number;
653      */
654     IndexRange indices() const {
655         return IndexRange(&this);
656     }
657 
658     protected struct IndexRange {
659     @nogc:
660         private {
661             Members.Range range;
662             bool _error;
663         }
664         this(const(HiBONT*) owner) {
665             range = owner.opSlice;
666         }
667 
668         ~this() {
669             range.dispose;
670         }
671 
672         @property bool empty() const pure {
673             return range.empty;
674         }
675 
676         @property void popFront() {
677             range.popFront;
678         }
679 
680         uint front() {
681             uint index;
682             if (!is_index(range.front.key, index)) {
683                 _error = true;
684             }
685             return index;
686         }
687 
688         @property error() const pure {
689             return _error;
690         }
691     }
692 
693     /**
694      * Check if the HiBON is an Array
695      * @return true if all keys is indices and are consecutive
696      */
697     bool isArray() const {
698         auto range = indices;
699         long prev_index = -1;
700         while (!range.empty) {
701             const index = range.front;
702             if (range.error || (prev_index + 1 != index)) {
703                 return false;
704             }
705             prev_index = index;
706             range.popFront;
707         }
708         return true;
709     }
710 
711     int last_index() {
712         int result = -1;
713         auto range = indices;
714         while (!range.empty) {
715             const index = range.front;
716             if (!range.error) {
717                 result = index;
718             }
719             range.popFront;
720         }
721         return result;
722     }
723 
724     void opOpAssign(string op)(ref HiBONT cat) if (op == "~") {
725         const index = cast(uint)(last_index + 1);
726         this[index] = cat;
727     }
728 
729     void opOpAssign(string op, T)(T cat) if (op == "~") {
730         const index = cast(uint)(last_index + 1);
731         this[index] = cat;
732     }
733 
734     ///
735     // unittest {
736     // {
737     //     auto hibon = HiBON();
738     //     assert(hibon.isArray);
739 
740     //     hibon["0"] = 1;
741     //     assert(hibon.isArray);
742     //     hibon["1"] = 2;
743     //     assert(hibon.isArray);
744     //     hibon["2"] = 3;
745     //     assert(hibon.isArray);
746     //     hibon["x"] = 3;
747     //     assert(!hibon.isArray);
748     // }
749     // {
750     //     auto hibon = HiBON();
751     //     hibon["1"] = 1;
752     //     assert(!hibon.isArray);
753     //     hibon["0"] = 2;
754     //     assert(hibon.isArray);
755     //     hibon["4"] = 3;
756     //     assert(!hibon.isArray);
757     //     hibon["3"] = 4;
758     //     assert(!hibon.isArray);
759     //     hibon["2"] = 7;
760     //     assert(hibon.isArray);
761     //     hibon["05"] = 2;
762     //     assert(!hibon.isArray);
763     // }
764     // }
765 
766     // unittest {
767     //     struct Table {
768     //         bool BOOLEAN;
769     //         float FLOAT32;
770     //         double FLOAT64;
771     //         //     BigNumberBIGINT;
772 
773     //         int INT32;
774     //         long INT64;
775     //         uint UINT32;
776     //         ulong UINT64;
777     //     }
778 
779     //     Table table;
780     //     table.FLOAT32 = 1.23;
781     //     table.FLOAT64 = 1.23e200;
782     //     table.INT32 = -42;
783     //     table.INT64 = -0x0123_3456_789A_BCDF;
784     //     table.UINT32 = 42;
785     //     table.UINT64 = 0x0123_3456_789A_BCDF;
786     //     table.BOOLEAN = true;
787     //     auto test_table = table.tupleof;
788     //     //test_tabel.BIGINT   = BigNumber("-1234_5678_9123_1234_5678_9123_1234_5678_9123");
789 
790     //     { // empty
791     //         auto hibon = HiBON();
792     //         assert(hibon.length is 0);
793 
794     //         assert(hibon.size is ubyte.sizeof);
795     //         immutable data = hibon.serialize;
796 
797     //         const doc = Document(data);
798     //         assert(doc.length is 0);
799     //         assert(doc[].empty);
800     //     }
801 
802     //     // Note that the keys are in alphabetic order
803     //     // Because the HiBON keys must be ordered
804     //     { // Single element
805     //         auto hibon = HiBON();
806     //         enum pos = 1;
807     //         alias A = typeof(test_table[pos]);
808 
809     //         static assert(is(typeof(test_table[pos]) == float));
810     //         alias M = typeof(test_table[pos]);
811     //         enum key = basename!(table.tupleof[pos]);
812 
813     //         hibon[key] = test_table[pos];
814 
815     //         assert(hibon.length is 1);
816 
817     //         const m = hibon[key];
818     //         assert(m.type is Type.FLOAT32);
819     //         assert(m.key == key);
820     //         assert(m.get!(M) == test_table[pos]);
821     //         assert(m.by!(Type.FLOAT32) == test_table[pos]);
822 
823     //         immutable size = hibon.serialize_size;
824     //         // This size of a HiBON with as single element of the type FLOAT32
825     //         enum hibon_size
826     //             = LEB128.calc_size(14) // Size of the object in ubytes (uint(14))
827     //             + Type.sizeof // The HiBON Type  (Type.FLOAT32)  1
828     //             + ubyte.sizeof // Length of the key (ubyte(7))    2
829     //             + Type.FLOAT32.stringof.length // The key text string ("FLOAT32") 9
830     //             + float.sizeof // The data            (float(1.23)) 13
831     //             ;
832 
833     //         const doc_size = Document.sizeT(Type.FLOAT32, Type.FLOAT32.stringof, test_table[pos]);
834 
835     //         assert(size is hibon_size);
836     //         assert(size is LEB128.calc_size(14) + doc_size);
837 
838     //         immutable data = hibon.serialize;
839 
840     //         const doc = Document(data);
841 
842     //         assert(doc.length is 1);
843     //         const e = doc[key];
844 
845     //         assert(e.type is Type.FLOAT32);
846     //         Text key_text;
847     //         assert(e.key(key_text) == key);
848     //         assert(e.by!(Type.FLOAT32) == test_table[pos]);
849 
850     //     }
851 
852     //     { // HiBON Test for basic types
853     //         auto hibon = HiBON();
854 
855     //         string[test_table.length] keys;
856     //         foreach (i, t; test_table) {
857     //             enum key = basename!(table.tupleof[i]);
858 
859     //             hibon[key] = t;
860     //             keys[i] = key;
861     //         }
862 
863     //         size_t index;
864     //         foreach (m; hibon[]) {
865     //             assert(m.key == keys[index]);
866     //             index++;
867     //         }
868 
869     //         foreach (i, t; test_table) {
870     //             enum key = basename!(table.tupleof[i]);
871 
872     //             const m = hibon[key];
873     //             assert(m.key == key);
874     //             alias U = typeof(t);
875     //             assert(m.get!(U) == t);
876     //         }
877 
878     //         immutable data = hibon.serialize;
879     //         const doc = Document(data);
880 
881     //         assert(doc.length is test_table.length);
882 
883     //         foreach (i, t; test_table) {
884     //             enum key = basename!(table.tupleof[i]);
885     //             const e = doc[key];
886     //             Text key_text;
887     //             assert(e.key(key_text) == key);
888     //             alias U = typeof(t);
889     //             assert(e.get!(U) == t);
890     //         }
891     //     }
892     // }
893 
894     // unittest {
895     //     struct TableArray {
896     //         ubyte[] BINARY;
897     //         // bool[]  BOOLEAN_ARRAY;
898     //         // float[] FLOAT32_ARRAY;
899     //         // double[]FLOAT64_ARRAY;
900     //         // int[]   INT32_ARRAY;
901     //         // long[]  INT64_ARRAY;
902     //         char[] STRING;
903     //         // uint[]  UINT32_ARRAY;
904     //         // ulong[] UINT64_ARRAY;
905 
906     //     }
907 
908     //     TableArray table_array;
909     //     const(ubyte[3]) binary = [1, 2, 3];
910     //     table_array.BINARY.create(binary);
911     //     // const(float[3]) float32_array=[-1.23, 3, 20e30];
912     //     // table_array.FLOAT32_ARRAY.create(float32_array);
913     //     // const(double[2]) float64_array=[10.3e200, -1e-201];
914     //     // table_array.FLOAT64_ARRAY.create(float64_array);
915     //     // const(int[4]) int32_array=[-11, -22, 33, 44];
916     //     // table_array.INT32_ARRAY.create(int32_array);
917     //     // const(long[4]) int64_array=[0x17, 0xffff_aaaa, -1, 42];
918     //     // table_array.INT64_ARRAY.create(int64_array);
919     //     // const(uint[4]) uint32_array=[11, 22, 33, 44];
920     //     // table_array.UINT32_ARRAY.create(uint32_array);
921     //     // const(ulong[4]) uint64_array=[0x17, 0xffff_aaaa, 1, 42];
922     //     // table_array.UINT64_ARRAY.create(uint64_array);
923     //     // const(bool[2]) boolean_array=[true, false];
924     //     // table_array.BOOLEAN_ARRAY.create(boolean_array);
925     //     const(char[4]) text = "Text";
926     //     table_array.STRING.create(text);
927 
928     //     auto test_table_array = table_array.tupleof;
929     //     scope (exit) {
930     //         foreach (i, t; test_table_array) {
931 
932     //                 .dispose(t);
933     //         }
934     //     }
935 
936     //     { // HiBON Test for basic-array types
937     //         auto hibon = HiBON();
938 
939     //         string[test_table_array.length] keys;
940     //         foreach (i, t; test_table_array) {
941     //             enum key = basename!(table_array.tupleof[i]);
942     //             hibon[key] = cast(immutable) t;
943     //             keys[i] = key;
944     //         }
945 
946     //         size_t index;
947     //         foreach (m; hibon[]) {
948     //             assert(m.key == keys[index]);
949     //             index++;
950     //         }
951 
952     //         foreach (i, t; test_table_array) {
953     //             enum key = basename!(table_array.tupleof[i]);
954     //             const m = hibon[key];
955     //             assert(m.key == key);
956     //             alias U = immutable(typeof(t));
957     //             assert(m.get!(U) == t);
958     //         }
959 
960     //         immutable data = hibon.serialize;
961     //         const doc = Document(data);
962 
963     //         assert(doc.length is test_table_array.length);
964 
965     //         foreach (i, t; test_table_array) {
966     //             enum key = basename!(table_array.tupleof[i]);
967     //             const e = doc[key];
968     //             Text key_text;
969     //             assert(e.key(key_text) == key);
970     //             alias U = immutable(typeof(t));
971     //             assert(e.get!(U) == t);
972     //         }
973 
974     //     }
975     // }
976 
977     // unittest { // HIBON test containg an child HiBON
978     //     auto hibon = HiBON();
979     //     auto hibon_child = HiBON();
980     //     enum child_name = "child";
981 
982     //     hibon["string"] = "Text";
983     //     hibon["float"] = float(1.24);
984 
985     //     immutable hibon_size_no_child = hibon.serialize_size;
986     //     hibon_child["int32"] = 42;
987     //     immutable hibon_child_size = hibon_child.serialize_size;
988     //     hibon[child_name] = hibon_child;
989 
990     //     immutable child_key_size = Document.sizeKey(child_name);
991     //     immutable hibon_size = hibon.serialize_size;
992     //     assert(hibon_size is hibon_size_no_child + child_key_size + hibon_child_size);
993 
994     //     immutable data = hibon.serialize;
995 
996     //     assert(data.length is hibon_size);
997     //     const doc = Document(data);
998 
999     // }
1000 
1001     // unittest { // Use of native Documet in HiBON
1002     //     auto native_hibon = HiBON();
1003     //     native_hibon["int"] = int(42);
1004     //     immutable native_data = native_hibon.serialize;
1005     //     auto native_doc = Document(native_hibon.serialize);
1006 
1007     //     auto hibon = HiBON();
1008     //     hibon["string"] = "Text";
1009 
1010     //     immutable hibon_no_native_document_size = hibon.size;
1011     //     hibon["native"] = native_doc;
1012     //     immutable data = hibon.serialize;
1013     //     const doc = Document(data);
1014 
1015     //     {
1016     //         const e = doc["string"];
1017     //         assert(e.type is Type.STRING);
1018     //         assert(e.get!string == "Text");
1019     //     }
1020 
1021     //     { // Check native document
1022     //         const e = doc["native"];
1023 
1024     //         assert(e.type is Type.DOCUMENT);
1025     //         const sub_doc = e.get!Document;
1026     //         assert(sub_doc.length is 1);
1027     //         assert(sub_doc.data == native_data);
1028     //         const sub_e = sub_doc["int"];
1029     //         assert(sub_e.type is Type.INT32);
1030     //         assert(sub_e.get!int  is 42);
1031     //     }
1032     // }
1033 
1034     // unittest { // Document array
1035     //     import std.typecons : Tuple, isTuple;
1036 
1037     //     auto hibon_array = HiBON();
1038     //     alias TabelDocArray = Tuple!(
1039     //             int, "a",
1040     //             bool, "b",
1041     //             float, "c"
1042     //     );
1043     //     TabelDocArray tabel_doc_array;
1044     //     tabel_doc_array.a = 42;
1045     //     tabel_doc_array.b = true;
1046     //     tabel_doc_array.c = 42.42;
1047 
1048     //     foreach (i, t; tabel_doc_array) {
1049     //         enum name = tabel_doc_array.fieldNames[i];
1050     //         auto local_hibon = HiBON();
1051     //         local_hibon[name] = t;
1052     //         if (i < 1) {
1053     //             hibon_array ~= local_hibon;
1054     //         }
1055     //     }
1056 
1057     //     auto hibon = HiBON();
1058     //     hibon["int"] = int(42);
1059     //     hibon["array"] = hibon_array;
1060 
1061     //     immutable data = hibon.serialize;
1062 
1063     //     const doc = Document(data);
1064 
1065     //     {
1066     //         assert(doc["int"].get!int  is 42);
1067     //     }
1068 
1069     // }
1070 
1071     // unittest { // Check empty/null object
1072     // {
1073     //         auto hibon = HiBON();
1074     //         auto sub = HiBON();
1075     //         assert(sub.size == ubyte.sizeof);
1076     //         const sub_doc = Document(sub.serialize);
1077     //         hibon["a"] = sub_doc;
1078     //         assert(hibon.size == Type.sizeof + ubyte.sizeof + "a".length + sub.size);
1079 
1080     //     }
1081 
1082     //     {
1083     //         auto hibon = HiBON();
1084     //         auto sub = HiBON();
1085     //         assert(sub.size == ubyte.sizeof);
1086     //         hibon["a"] = sub;
1087     //         assert(hibon.size == Type.sizeof + ubyte.sizeof + "a".length + sub.size);
1088     //     }
1089     // }
1090 
1091 }