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 }