1 module mysql.packet; 2 3 4 import std.algorithm; 5 import std.traits; 6 7 import mysql.exception; 8 9 10 struct InputPacket { 11 @disable this(); 12 13 this(ubyte[]* buffer) { 14 buffer_ = buffer; 15 in_ = *buffer_; 16 } 17 18 T peek(T)() if (!isArray!T) { 19 assert(T.sizeof <= in_.length); 20 return *(cast(T*)in_.ptr); 21 } 22 23 T eat(T)() if (!isArray!T) { 24 assert(T.sizeof <= in_.length); 25 auto ptr = cast(T*)in_.ptr; 26 in_ = in_[T.sizeof..$]; 27 return *ptr; 28 } 29 30 T peek(T)(size_t count) if (isArray!T) { 31 alias ValueType = typeof(Type.init[0]); 32 33 assert(ValueType.sizeof * count <= in_.length); 34 auto ptr = cast(ValueType*)in_.ptr; 35 return ptr[0..count]; 36 } 37 38 T eat(T)(size_t count) if (isArray!T) { 39 alias ValueType = typeof(T.init[0]); 40 41 assert(ValueType.sizeof * count <= in_.length); 42 auto ptr = cast(ValueType*)in_.ptr; 43 in_ = in_[ValueType.sizeof * count..$]; 44 return ptr[0..count]; 45 } 46 47 void expect(T)(T x) { 48 if (x != eat!T) 49 throw new MySQLProtocolException("Bad packet format"); 50 } 51 52 void skip(size_t count) { 53 assert(count <= in_.length); 54 in_ = in_[count..$]; 55 } 56 57 auto countUntil(ubyte x, bool expect) { 58 auto index = in_.countUntil(x); 59 if (expect) { 60 if ((index < 0) || (in_[index] != x)) 61 throw new MySQLProtocolException("Bad packet format"); 62 } 63 return index; 64 } 65 66 ulong eatLenEnc() { 67 auto header = eat!ubyte; 68 if (header < 0xfb) 69 return header; 70 71 ulong lo; 72 ulong hi; 73 74 switch(header) { 75 case 0xfb: 76 return 0; 77 case 0xfc: 78 return eat!ushort; 79 case 0xfd: 80 lo = eat!ubyte; 81 hi = eat!ushort; 82 return lo | (hi << 8); 83 case 0xfe: 84 lo = eat!uint; 85 hi = eat!uint; 86 return lo | (hi << 32); 87 default: 88 throw new MySQLProtocolException("Bad packet format"); 89 } 90 } 91 92 auto remaining() const { 93 return in_.length; 94 } 95 96 bool empty() const { 97 return in_.length == 0; 98 } 99 protected: 100 ubyte[]* buffer_; 101 ubyte[] in_; 102 } 103 104 105 struct OutputPacket { 106 @disable this(); 107 108 this(ubyte[]* buffer) { 109 buffer_ = buffer; 110 out_ = buffer_.ptr + 4; 111 } 112 113 void put(T)(T x) if (!isArray!T) { 114 put(offset_, x); 115 } 116 117 void put(T)(T x) if (isArray!T) { 118 put(offset_, x); 119 } 120 121 void put(T)(size_t offset, T x) if (!isArray!T) { 122 grow(offset, T.sizeof); 123 124 *(cast(T*)(out_ + offset)) = x; 125 offset_ = max(offset + T.sizeof, offset_); 126 } 127 128 void put(T)(size_t offset, T x) if (isArray!T) { 129 alias ValueType = Unqual!(typeof(T.init[0])); 130 131 grow(offset, ValueType.sizeof * x.length); 132 133 (cast(ValueType*)(out_ + offset))[0..x.length] = x; 134 offset_ = max(offset + (ValueType.sizeof * x.length), offset_); 135 } 136 137 void putLenEnc(ulong x) { 138 if (x < 0xfb) { 139 put!ubyte(cast(ubyte)x); 140 } else if (x <= ushort.max) { 141 put!ubyte(0xfc); 142 put!ushort(cast(ushort)x); 143 } else if (x <= (uint.max >> 8)) { 144 put!ubyte(0xfd); 145 put!ubyte(cast(ubyte)(x)); 146 put!ushort(cast(ushort)(x >> 8)); 147 } else { 148 put!ubyte(0xfe); 149 put!uint(cast(uint)x); 150 put!uint(cast(uint)(x >> 32)); 151 } 152 } 153 154 size_t marker(T)() if (!isArray!T) { 155 grow(offset_, T.sizeof); 156 157 auto place = offset_; 158 offset_ += T.sizeof; 159 return place; 160 } 161 162 size_t marker(T)(size_t count) if (isArray!T) { 163 alias ValueType = Unqual!(typeof(T.init[0])); 164 grow(offset_, ValueType.sizeof * x.length); 165 166 auto place = offset_; 167 offset_ += (ValueType.sizeof * x.length); 168 return place; 169 } 170 171 void finalize(ubyte seq) { 172 if (offset_ >= 0xffffff) 173 throw new MySQLConnectionException("Packet size exceeds 2^24"); 174 uint length = cast(uint)offset_; 175 uint header = cast(uint)((offset_ & 0xffffff) | (seq << 24)); 176 *(cast(uint*)buffer_.ptr) = header; 177 } 178 179 void finalize(ubyte seq, size_t extra) { 180 if (offset_ + extra >= 0xffffff) 181 throw new MySQLConnectionException("Packet size exceeds 2^24"); 182 uint length = cast(uint)(offset_ + extra); 183 uint header = cast(uint)((length & 0xffffff) | (seq << 24)); 184 *(cast(uint*)buffer_.ptr) = header; 185 } 186 187 void reset() { 188 offset_ = 0; 189 } 190 void reserve(size_t size) { 191 (*buffer_).length = max((*buffer_).length, 4 + size); 192 out_ = buffer_.ptr + 4; 193 } 194 195 void fill(ubyte x, size_t size) { 196 grow(offset_, size); 197 out_[offset_..offset_ + size] = 0; 198 offset_ += size; 199 } 200 201 size_t length() const { 202 return offset_; 203 } 204 205 bool empty() const { 206 return offset_ == 0; 207 } 208 209 const(ubyte)[] get() const { 210 return (*buffer_)[0..4 + offset_]; 211 } 212 protected: 213 void grow(size_t offset, size_t size) { 214 auto requested = 4 + offset + size; 215 if (requested > buffer_.length) { 216 auto capacity = (*buffer_).capacity; 217 while (capacity < requested) 218 capacity <<= 1; 219 buffer_.length = requested; 220 out_ = buffer_.ptr + 4; 221 } 222 } 223 ubyte[]* buffer_; 224 ubyte* out_; 225 size_t offset_ = 0; 226 }