1 module fdb.tuple.packer;
2 
3 import
4     std.algorithm,
5     std.exception,
6     std.math,
7     std.range,
8     std..string,
9     std.traits,
10     std.uuid;
11 
12 import
13     fdb.tuple.integral,
14     fdb.tuple.part,
15     fdb.tuple.segmented,
16     fdb.tuple.tupletype;
17 
18 private struct Packer
19 {
20     ubyte[] bytes;
21 
22     void write(typeof(null))
23     {
24         bytes ~= TupleType.Nil;
25     }
26 
27     void write(in ubyte[] value)
28     {
29         bytes ~= TupleType.Bytes;
30         bytes ~= value
31             .map!(a => a ? [a] : cast(ubyte[])[0x00, byteArrayEndMarker])
32             .reduce!"a ~ b";
33         bytes ~= byteArrayEndMarker;
34     }
35 
36     void write(in string value)
37     {
38         bytes ~= TupleType.Utf8;
39         bytes ~= cast(ubyte[])(value.toStringz[0..value.length + 1]);
40     }
41 
42     void write(T)(in T value)
43     if (isIntegral!T)
44     in
45     {
46         enforce(value >= long.min,
47             "Value cannot exceed minumum 64-bit signed integer");
48         enforce(value <= long.max,
49             "Value cannot exceed maximum 64-bit signed integer");
50     }
51     body
52     {
53         auto size   = value.minsizeof;
54         auto marker =
55             cast(ubyte)(TupleType.IntBase + ((value > 0) ? size : -size));
56 
57         ulong compliment = (value > 0) ? value : ~(-value);
58         auto segmented   = Segmented!ulong(compliment);
59 
60         bytes ~= marker;
61         bytes ~= segmented.segments[0..size].retro.array;
62     }
63 
64     void write(T)(in T value)
65     if (isFloatingPoint!T)
66     {
67         auto filtered = (!value.isNaN) ? value : T.nan;
68         static if (is(T == float))
69         {
70             auto segmented  = Segmented!(T, ubyte, uint)(filtered);
71             auto mask       = floatSignMask;
72 
73             bytes ~= TupleType.Single;
74         }
75         else static if (is(T == double))
76         {
77             auto segmented  = Segmented!(T, ubyte, ulong)(filtered);
78             auto mask       = doubleSignMask;
79 
80             bytes ~= TupleType.Double;
81         }
82         else
83             static assert(0, "Type " ~ T.stringof ~ "is not supported");
84 
85         // check if value is positive or negative
86         if ((segmented.alt & mask) == 0)
87             segmented.alt |= mask;
88         else // negative
89             segmented.alt  = ~segmented.alt;
90 
91         bytes ~= segmented.segments[].retro.array;
92     }
93 
94     void write(in UUID value)
95     in
96     {
97         enforce(!value.data.empty);
98     }
99     body
100     {
101         bytes ~= TupleType.Uuid128;
102         bytes ~= value.data;
103     }
104 
105     void write(R)(in R r)
106     if(isInputRange!R && !is(R == string))
107     {
108         foreach (e; r)
109             write(e);
110     }
111 
112     void write(in Part part)
113     {
114         foreach (T; Part.AllowedTypes)
115             if (auto v = part.peek!T)
116             {
117                 write(*v);
118                 return;
119             }
120         enforce(0, "Type " ~ part.type.toString ~ " is not supported");
121     }
122 }
123 
124 auto pack(T...)(T parts)
125 {
126     Packer w;
127     foreach (p; parts)
128         w.write(p);
129     return w.bytes.idup;
130 }