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 @property auto size() 23 { 24 if (type.isFDBIntegral || 25 type.isFDBFloat || 26 type.isFDBDouble || 27 type.isFDBUUID) 28 { 29 return type.FDBsizeof; 30 } 31 else 32 { 33 auto size = (cast(char[])slice).indexOf(0, 0); 34 return (size > 0) ? cast(ulong)size + 1 : 0; 35 } 36 } 37 38 auto static create(Range)( 39 const TupleType type, 40 Range slice) pure 41 if (isInputRange!(Unqual!Range)) 42 in 43 { 44 if (type.isFDBIntegral || 45 type.isFDBFloat || 46 type.isFDBDouble || 47 type.isFDBUUID) 48 { 49 enforce(type.FDBsizeof == slice.length); 50 } 51 } 52 body 53 { 54 return FDBVariant(type, slice); 55 } 56 57 auto static create(Range)( 58 const TupleType type, 59 Range buffer, 60 const ulong offset) pure 61 if (isInputRange!(Unqual!Range)) 62 { 63 if (type.isFDBIntegral) 64 { 65 auto size = type.FDBsizeof; 66 enforce(offset + size <= buffer.length); 67 return FDBVariant( 68 type, 69 cast(shared)buffer[offset .. offset + size]); 70 } 71 return FDBVariant(type, cast(shared)buffer[offset .. $]); 72 } 73 74 auto isTypeOf(T)() const 75 { 76 static if (is(T == typeof(null))) 77 return type == TupleType.Nil; 78 else static if (is(T == ubyte[])) 79 return type == TupleType.Bytes; 80 else static if (is(T == string)) 81 return type == TupleType.Utf8; 82 else static if (is(T == long)) 83 return type.isFDBIntegral; 84 else static if (is(T == float)) 85 return type.isFDBFloat; 86 else static if (is(T == double)) 87 return type.isFDBDouble; 88 else static if (is(T == UUID)) 89 return type.isFDBUUID; 90 else 91 static assert(0, "Type " ~ T.to!string ~ " is not supported"); 92 } 93 94 auto get(T)() const 95 in 96 { 97 enforce(isTypeOf!T); 98 } 99 body 100 { 101 static if (is(T == typeof(null))) 102 return getNull; 103 else static if (is(T == ubyte[])) 104 return getBytes; 105 else static if (is(T == string)) 106 return getStr; 107 else static if (is(T == long)) 108 return getInt; 109 else static if (is(T == float)) 110 return getFloat!(float, uint, floatSignMask); 111 else static if (is(T == double)) 112 return getFloat!(double, ulong, doubleSignMask); 113 else static if (is(T == UUID)) 114 return getUUID; 115 else 116 static assert(0, "Type " ~ T.to!string ~ " is not supported"); 117 } 118 119 private auto getNull() const pure @nogc 120 { 121 return null; 122 } 123 124 private auto getBytes() const 125 in 126 { 127 enforce(slice.length > 1); 128 enforce(slice[$ - 1] == byteArrayEndMarker); 129 } 130 body 131 { 132 ubyte[] result; 133 foreach(i, b; slice[0..$-1]) 134 if (b != byteArrayEndMarker || i == 0 || slice[i - 1] != 0x00) 135 result ~= b; 136 return result; 137 } 138 139 private auto getStr() const 140 { 141 auto chars = (cast(char[])slice); 142 auto size = chars.indexOf(0, 0); 143 if (size > 0) 144 chars = chars[0..size]; 145 return chars.to!string; 146 } 147 148 private auto getInt() const 149 { 150 Segmented!ulong dbValue; 151 dbValue.segments[0..slice.length] = slice.retro.array; 152 153 long value; 154 if (type < TupleType.IntBase) 155 { 156 value = -(~dbValue.value); 157 auto size = type.FDBsizeof; 158 if (size < long.sizeof) 159 value |= (-1L << (size << 3)); 160 } 161 else 162 value = dbValue.value; 163 return value; 164 } 165 166 private auto getFloat(F, I, alias M)() const 167 { 168 Segmented!(F, ubyte, I) dbValue; 169 dbValue.segments[0..slice.length] = slice.retro.array; 170 171 // check if value is positive or negative 172 if ((dbValue.alt & M) != 0) 173 dbValue.alt ^= M; 174 else // negative 175 dbValue.alt = ~dbValue.alt; 176 177 auto value = dbValue.value; 178 return value; 179 } 180 181 private auto getUUID() const 182 { 183 UUID value; 184 value.data = slice[0..$]; 185 return value; 186 } 187 } 188 189 alias variant = FDBVariant.create; 190 191 auto unpack(Range)(Range bytes) 192 if (isInputRange!(Unqual!Range)) 193 { 194 ulong pos = 0; 195 Part[] parts; 196 while (pos < bytes.length) 197 { 198 auto marker = cast(TupleType)bytes[pos++]; 199 auto var = variant(marker, bytes, pos); 200 201 Part part; 202 foreach (T; Part.AllowedTypes) 203 if (var.isTypeOf!T) 204 { 205 part = var.get!T; 206 break; 207 } 208 209 parts ~= part; 210 pos += var.size; 211 } 212 return parts; 213 } 214 215 /** 216 * Returns single value if type matches T 217 */ 218 auto unpack(T, Range)(Range bytes) 219 { 220 auto unpacked = unpack(bytes); 221 auto value = unpacked.front.get!T; 222 return value; 223 }