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 }