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 }