1 module fdb.tuple.unpacker; 2 3 import 4 std.array, 5 std.conv, 6 std.exception, 7 std.range, 8 std..string, 9 std.traits, 10 std.uuid; 11 12 import 13 fdb.tuple.part, 14 fdb.tuple.segmented, 15 fdb.tuple.tupletype; 16 17 private struct FDBVariant 18 { 19 const TupleType type; 20 const shared ubyte[] slice; 21 22 private ulong _length; 23 @property auto length() 24 { 25 return _length; 26 } 27 28 private Part _part; 29 @property Part part() 30 { 31 if (_part.hasValue) 32 return _part; 33 34 switch (type) with (TupleType) 35 { 36 case Nil: 37 _part = readNull; 38 break; 39 case Bytes: 40 _part = readBytes; 41 break; 42 case Utf8: 43 _part = readStr; 44 break; 45 case IntNeg8: .. case IntPos8: 46 _part = readInt; 47 break; 48 case Single: 49 _part = readFloat!(float, uint, floatSignMask); 50 break; 51 case Double: 52 _part = readFloat!(double, ulong, doubleSignMask); 53 break; 54 case Uuid128: 55 _part = readUUID; 56 break; 57 default: 58 enforce(0, "Type " ~ type.to!string ~ " is not supported"); 59 break; 60 } 61 62 return _part; 63 } 64 65 this(Range)(in TupleType type, Range slice) pure 66 if (isInputRange!(Unqual!Range)) 67 in 68 { 69 with (type) 70 if (isFDBIntegral || isFDBFloat || isFDBDouble || isFDBUUID) 71 enforce(FDBsizeof == slice.length); 72 } 73 body 74 { 75 this.type = type; 76 this.slice = slice; 77 } 78 79 this(Range)(in TupleType type, Range buffer, in ulong offset) pure 80 if (isInputRange!(Unqual!Range)) 81 { 82 if (type == TupleType.Nil || 83 type.isFDBIntegral || 84 type.isFDBFloat || 85 type.isFDBDouble || 86 type.isFDBUUID) 87 { 88 auto size = type.FDBsizeof; 89 enforce(offset + size <= buffer.length); 90 91 this.type = type; 92 this.slice = cast(shared)buffer[offset .. offset + size]; 93 this._length = size; 94 } 95 else 96 { 97 this.type = type; 98 this.slice = cast(shared)buffer[offset .. $]; 99 } 100 } 101 102 private auto readNull() const pure @nogc 103 { 104 return null; 105 } 106 107 private auto readBytes() 108 in 109 { 110 enforce(slice.length > 1); 111 } 112 body 113 { 114 ubyte[] result; 115 foreach(idx, b; slice) 116 if (b != byteArrayEndMarker) 117 result ~= b; 118 else if (idx > 0 && slice[idx - 1] != 0x00) 119 { 120 _length = idx + 1; 121 break; 122 } 123 124 return result; 125 } 126 127 private auto readStr() 128 { 129 auto chars = (cast(char[])slice); 130 auto size = chars.indexOf(0, 0); 131 if (size > -1) 132 { 133 chars = chars[0..size]; 134 _length = size + 1; 135 } 136 return chars.to!string; 137 } 138 139 private auto readInt() const 140 { 141 Segmented!ulong dbValue; 142 dbValue.segments[0..slice.length] = slice.retro.array; 143 144 long value; 145 if (type < TupleType.IntBase) 146 { 147 value = -(~dbValue.value); 148 auto size = type.FDBsizeof; 149 if (size < long.sizeof) 150 value |= (-1L << (size << 3)); 151 } 152 else 153 value = dbValue.value; 154 return value; 155 } 156 157 private auto readFloat(F, I, alias M)() const 158 { 159 Segmented!(F, ubyte, I) dbValue; 160 dbValue.segments[0..slice.length] = slice.retro.array; 161 162 // check if value is positive or negative 163 if ((dbValue.alt & M) != 0) 164 dbValue.alt ^= M; 165 else // negative 166 dbValue.alt = ~dbValue.alt; 167 168 auto value = dbValue.value; 169 return value; 170 } 171 172 private auto readUUID() const 173 { 174 UUID value; 175 value.data = slice[0..$]; 176 return value; 177 } 178 } 179 180 auto unpack(Range)(Range bytes) 181 if (isInputRange!(Unqual!Range)) 182 { 183 ulong pos = 0; 184 Part[] parts; 185 while (pos < bytes.length) 186 { 187 auto marker = cast(TupleType)bytes[pos++]; 188 auto var = FDBVariant(marker, bytes, pos); 189 190 parts ~= var.part; 191 pos += var.length; 192 } 193 return parts; 194 } 195 196 /** 197 * Returns single value if type matches T 198 */ 199 auto unpack(T, size_t i = 0, Range)(Range bytes) 200 { 201 auto unpacked = unpack(bytes); 202 auto value = unpacked[i].get!T; 203 return value; 204 }