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 }