1 module pgsql.packet; 2 3 4 import std.algorithm; 5 import std.bitmanip; 6 import std.traits; 7 8 import pgsql.exception; 9 10 11 pragma(inline, true) T host(T)(T x) if (isScalarType!T) { 12 static if (T.sizeof > 1) { 13 return *cast(T*)(nativeToBigEndian(x).ptr); 14 } else { 15 return x; 16 } 17 } 18 19 20 pragma(inline, true) T native(T)(T x) if (isScalarType!T) { 21 static if (T.sizeof > 1) { 22 return bigEndianToNative!T(*cast(ubyte[T.sizeof]*)&x); 23 } else { 24 return x; 25 } 26 } 27 28 29 pragma(inline, true) T native(T)(ubyte* ptr) if (isScalarType!T) { 30 static if (T.sizeof > 1) { 31 return bigEndianToNative!T(*cast(ubyte[T.sizeof]*)ptr); 32 } else { 33 return x.ptr[0]; 34 } 35 } 36 37 38 39 struct InputPacket { 40 @disable this(); 41 42 this(ubyte type, ubyte[]* buffer) { 43 type_ = type; 44 buffer_ = buffer; 45 in_ = *buffer_; 46 } 47 48 ubyte type() const { 49 return type_; 50 } 51 52 T peek(T)() if (!isArray!T) { 53 assert(T.sizeof <= in_.length); 54 return native(*(cast(T*)in_.ptr)); 55 } 56 57 T eat(T)() if (!isArray!T) { 58 assert(T.sizeof <= in_.length); 59 auto ptr = cast(T*)in_.ptr; 60 in_ = in_[T.sizeof..$]; 61 return native(*ptr); 62 } 63 64 const(char)[] peekz() { 65 import core.stdc.string : strlen; 66 return cast(const(char)[])in_[0..strlen(cast(char*)in_.ptr)]; 67 } 68 69 const(char)[] eatz() { 70 import core.stdc.string : strlen; 71 auto len = strlen(cast(char*)in_.ptr); 72 auto result = cast(const(char)[])in_[0..len]; 73 in_ = in_[len + 1..$]; 74 return result; 75 } 76 77 void skipz() { 78 import core.stdc.string : strlen; 79 auto len = strlen(cast(char*)in_.ptr); 80 in_ = in_[len + 1..$]; 81 } 82 83 T eat(T)(size_t count) if (isArray!T) { 84 alias ValueType = typeof(T.init[0]); 85 86 assert(ValueType.sizeof * count <= in_.length); 87 auto ptr = cast(ValueType*)in_.ptr; 88 in_ = in_[ValueType.sizeof * count..$]; 89 return ptr[0..count]; 90 } 91 92 void expect(T)(T x) { 93 if (x != eat!T) 94 throw new PgSQLProtocolException("Bad packet format"); 95 } 96 97 void skip(size_t count) { 98 assert(count <= in_.length); 99 in_ = in_[count..$]; 100 } 101 102 auto countUntil(ubyte x, bool expect) { 103 auto index = in_.countUntil(x); 104 if (expect) { 105 if ((index < 0) || (in_[index] != x)) 106 throw new PgSQLProtocolException("Bad packet format"); 107 } 108 return index; 109 } 110 111 void skipLenEnc() { 112 auto header = eat!ubyte; 113 if (header >= 0xfb) { 114 switch(header) { 115 case 0xfb: 116 return; 117 case 0xfc: 118 skip(2); 119 return; 120 case 0xfd: 121 skip(3); 122 return; 123 case 0xfe: 124 skip(8); 125 return; 126 default: 127 throw new PgSQLProtocolException("Bad packet format"); 128 } 129 } 130 } 131 132 ulong eatLenEnc() { 133 auto header = eat!ubyte; 134 if (header < 0xfb) 135 return header; 136 137 ulong lo; 138 ulong hi; 139 140 switch(header) { 141 case 0xfb: 142 return 0; 143 case 0xfc: 144 return eat!ushort; 145 case 0xfd: 146 lo = eat!ubyte; 147 hi = eat!ushort; 148 return lo | (hi << 8); 149 case 0xfe: 150 lo = eat!uint; 151 hi = eat!uint; 152 return lo | (hi << 32); 153 default: 154 throw new PgSQLProtocolException("Bad packet format"); 155 } 156 } 157 158 auto get() const { 159 return in_; 160 } 161 162 auto remaining() const { 163 return in_.length; 164 } 165 166 bool empty() const { 167 return in_.length == 0; 168 } 169 protected: 170 ubyte[]* buffer_; 171 ubyte[] in_; 172 ubyte type_; 173 } 174 175 176 struct OutputPacket { 177 @disable this(); 178 179 this(ubyte[]* buffer) { 180 buffer_ = buffer; 181 implicit_ = 4; 182 out_ = buffer_.ptr + 4; 183 } 184 185 this(ubyte type, ubyte[]* buffer) { 186 buffer_ = buffer; 187 implicit_ = 5; 188 if (buffer_.length < implicit_) 189 buffer_.length = implicit_; 190 *buffer_.ptr = type; 191 out_ = buffer_.ptr + implicit_; 192 } 193 194 void putz(const(char)[] x) { 195 put(x); 196 put!ubyte(0); 197 } 198 199 pragma(inline, true) void put(T)(T x) { 200 put(offset_, x); 201 } 202 203 pragma(inline, true) void put(T)(size_t offset, T x) if (!isArray!T) { 204 grow(offset, T.sizeof); 205 206 *(cast(T*)(out_ + offset)) = host(x); 207 offset_ = max(offset + T.sizeof, offset_); 208 } 209 210 void put(T)(size_t offset, T x) if (isArray!T) { 211 alias ValueType = Unqual!(typeof(T.init[0])); 212 213 grow(offset, ValueType.sizeof * x.length); 214 215 static if (ValueType.sizeof == 1) { 216 (cast(ValueType*)(out_ + offset))[0..x.length] = x; 217 } else { 218 auto pout = cast(ValueType*)(out_ + offset); 219 foreach (ref y; x) 220 *pout++ = host(y); 221 } 222 offset_ = max(offset + (ValueType.sizeof * x.length), offset_); 223 } 224 225 void putLenEnc(ulong x) { 226 if (x < 0xfb) { 227 put!ubyte(cast(ubyte)x); 228 } else if (x <= ushort.max) { 229 put!ubyte(0xfc); 230 put!ushort(cast(ushort)x); 231 } else if (x <= (uint.max >> 8)) { 232 put!ubyte(0xfd); 233 put!ubyte(cast(ubyte)(x)); 234 put!ushort(cast(ushort)(x >> 8)); 235 } else { 236 put!ubyte(0xfe); 237 put!uint(cast(uint)x); 238 put!uint(cast(uint)(x >> 32)); 239 } 240 } 241 242 size_t marker(T)() if (!isArray!T) { 243 grow(offset_, T.sizeof); 244 245 auto place = offset_; 246 offset_ += T.sizeof; 247 return place; 248 } 249 250 size_t marker(T)(size_t count) if (isArray!T) { 251 alias ValueType = Unqual!(typeof(T.init[0])); 252 grow(offset_, ValueType.sizeof * x.length); 253 254 auto place = offset_; 255 offset_ += (ValueType.sizeof * x.length); 256 return place; 257 } 258 259 void finalize() { 260 if ((offset_ + implicit_) > int.max) 261 throw new PgSQLConnectionException("Packet size exceeds 2^31"); 262 *(cast(uint*)(buffer_.ptr + implicit_ - 4)) = host(cast(uint)(4 + offset_)); 263 } 264 265 void finalize(ubyte) { 266 finalize(); 267 } 268 269 void finalize(ubyte seq, size_t extra) { 270 finalize(); 271 } 272 273 void reset() { 274 offset_ = 0; 275 } 276 void reserve(size_t size) { 277 (*buffer_).length = max((*buffer_).length, implicit_ + size); 278 out_ = buffer_.ptr + implicit_; 279 } 280 281 void fill(ubyte x, size_t size) { 282 grow(offset_, size); 283 out_[offset_..offset_ + size] = 0; 284 offset_ += size; 285 } 286 287 size_t length() const { 288 return offset_; 289 } 290 291 bool empty() const { 292 return offset_ == 0; 293 } 294 295 const(ubyte)[] get() const { 296 return (*buffer_)[0..implicit_ + offset_]; 297 } 298 protected: 299 void grow(size_t offset, size_t size) { 300 auto requested = implicit_ + offset + size; 301 if (requested > buffer_.length) { 302 auto capacity = max(128, (*buffer_).capacity); 303 while (capacity < requested) 304 capacity <<= 1; 305 buffer_.length = capacity; 306 out_ = buffer_.ptr + implicit_; 307 } 308 } 309 ubyte[]* buffer_; 310 311 ubyte* out_; 312 size_t offset_; 313 size_t implicit_; 314 }