1 module mysql.protocol.packet_helpers;
2 
3 import std.algorithm;
4 import std.conv;
5 import std.datetime;
6 import std.digest.sha;
7 import std.exception;
8 import std.range;
9 import std.socket;
10 import std.stdio;
11 import std.string;
12 import std.traits;
13 import std.variant;
14 
15 import mysql.common;
16 import mysql.protocol.constants;
17 import mysql.protocol.extra_types;
18 
19 /++
20 Function to extract a time difference from a binary encoded row.
21 
22 Time/date structures are packed by the server into a byte sub-packet
23 with a leading length byte, and a minimal number of bytes to embody the data.
24 
25 Params: a = slice of a protocol packet beginning at the length byte for a chunk of time data
26 
27 Returns: A populated or default initialized TimeDiff struct.
28 +/
29 TimeDiff toTimeDiff(in ubyte[] a) pure
30 {
31 	enforceEx!MYXProtocol(a.length, "Supplied byte array is zero length");
32 	TimeDiff td;
33 	uint l = a[0];
34 	enforceEx!MYXProtocol(l == 0 || l == 5 || l == 8 || l == 12, "Bad Time length in binary row.");
35 	if (l >= 5)
36 	{
37 		td.negative = (a[1]  != 0);
38 		td.days     = (a[5] << 24) + (a[4] << 16) + (a[3] << 8) + a[2];
39 	}
40 	if (l >= 8)
41 	{
42 		td.hours    = a[6];
43 		td.minutes  = a[7];
44 		td.seconds  = a[8];
45 	}
46 	// Note that the fractional seconds part is not stored by MySQL
47 	return td;
48 }
49 
50 /++
51 Function to extract a time difference from a text encoded column value.
52 
53 Text representations of a time difference are like -750:12:02 - 750 hours
54 12 minutes and two seconds ago.
55 
56 Params: s = A string representation of the time difference.
57 Returns: A populated or default initialized TimeDiff struct.
58 +/
59 TimeDiff toTimeDiff(string s)
60 {
61 	TimeDiff td;
62 	int t = parse!int(s);
63 	if (t < 0)
64 	{
65 		td.negative = true;
66 		t = -t;
67 	}
68 	td.hours    = cast(ubyte) t%24;
69 	td.days     = cast(ubyte) t/24;
70 	munch(s, ":");
71 	td.minutes  = parse!ubyte(s);
72 	munch(s, ":");
73 	td.seconds  = parse!ubyte(s);
74 	return td;
75 }
76 
77 /++
78 Function to extract a TimeOfDay from a binary encoded row.
79 
80 Time/date structures are packed by the server into a byte sub-packet
81 with a leading length byte, and a minimal number of bytes to embody the data.
82 
83 Params: a = slice of a protocol packet beginning at the length byte for a
84             chunk of time data.
85 Returns: A populated or default initialized std.datetime.TimeOfDay struct.
86 +/
87 TimeOfDay toTimeOfDay(in ubyte[] a) pure
88 {
89 	enforceEx!MYXProtocol(a.length, "Supplied byte array is zero length");
90 	uint l = a[0];
91 	enforceEx!MYXProtocol(l == 0 || l == 5 || l == 8 || l == 12, "Bad Time length in binary row.");
92 	enforceEx!MYXProtocol(l >= 8, "Time column value is not in a time-of-day format");
93 
94 	TimeOfDay tod;
95 	tod.hour    = a[6];
96 	tod.minute  = a[7];
97 	tod.second  = a[8];
98 	return tod;
99 }
100 
101 /++
102 Function to extract a TimeOfDay from a text encoded column value.
103 
104 Text representations of a time of day are as in 14:22:02
105 
106 Params: s = A string representation of the time.
107 Returns: A populated or default initialized std.datetime.TimeOfDay struct.
108 +/
109 TimeOfDay toTimeOfDay(string s)
110 {
111 	TimeOfDay tod;
112 	tod.hour = parse!int(s);
113 	enforceEx!MYXProtocol(tod.hour <= 24 && tod.hour >= 0, "Time column value is in time difference form");
114 	munch(s, ":");
115 	tod.minute = parse!ubyte(s);
116 	munch(s, ":");
117 	tod.second = parse!ubyte(s);
118 	return tod;
119 }
120 
121 /++
122 Function to pack a TimeOfDay into a binary encoding for transmission to the server.
123 
124 Time/date structures are packed into a string of bytes with a leading length
125 byte, and a minimal number of bytes to embody the data.
126 
127 Params: tod = TimeOfDay struct.
128 Returns: Packed ubyte[].
129 +/
130 ubyte[] pack(in TimeOfDay tod) pure nothrow
131 {
132 	ubyte[] rv;
133 	if (tod == TimeOfDay.init)
134 	{
135 		rv.length = 1;
136 		rv[0] = 0;
137 	}
138 	else
139 	{
140 		rv.length = 9;
141 		rv[0] = 8;
142 		rv[6] = tod.hour;
143 		rv[7] = tod.minute;
144 		rv[8] = tod.second;
145 	}
146 	return rv;
147 }
148 
149 /++
150 Function to extract a Date from a binary encoded row.
151 
152 Time/date structures are packed by the server into a byte sub-packet
153 with a leading length byte, and a minimal number of bytes to embody the data.
154 
155 Params: a = slice of a protocol packet beginning at the length byte for a
156             chunk of Date data.
157 Returns: A populated or default initialized std.datetime.Date struct.
158 +/
159 Date toDate(in ubyte[] a) pure
160 {
161 	enforceEx!MYXProtocol(a.length, "Supplied byte array is zero length");
162 	if (a[0] == 0)
163 		return Date(0,0,0);
164 
165 	enforceEx!MYXProtocol(a[0] >= 4, "Binary date representation is too short");
166 	int year    = (a[2]  << 8) + a[1];
167 	int month   = cast(int) a[3];
168 	int day     = cast(int) a[4];
169 	return Date(year, month, day);
170 }
171 
172 /++
173 Function to extract a Date from a text encoded column value.
174 
175 Text representations of a Date are as in 2011-11-11
176 
177 Params: s = A string representation of the time difference.
178 Returns: A populated or default initialized std.datetime.Date struct.
179 +/
180 Date toDate(string s)
181 {
182 	int year = parse!(ushort)(s);
183 	munch(s, "-");
184 	int month = parse!(ubyte)(s);
185 	munch(s, "-");
186 	int day = parse!(ubyte)(s);
187 	return Date(year, month, day);
188 }
189 
190 /++
191 Function to pack a Date into a binary encoding for transmission to the server.
192 
193 Time/date structures are packed into a string of bytes with a leading length
194 byte, and a minimal number of bytes to embody the data.
195 
196 Params: dt = std.datetime.Date struct.
197 Returns: Packed ubyte[].
198 +/
199 ubyte[] pack(in Date dt) pure nothrow
200 {
201 	ubyte[] rv;
202 	if (dt.year < 0)
203 	{
204 		rv.length = 1;
205 		rv[0] = 0;
206 	}
207 	else
208 	{
209 		rv.length = 5;
210 		rv[0] = 4;
211 		rv[1] = cast(ubyte) ( dt.year       & 0xff);
212 		rv[2] = cast(ubyte) ((dt.year >> 8) & 0xff);
213 		rv[3] = cast(ubyte)   dt.month;
214 		rv[4] = cast(ubyte)   dt.day;
215 	}
216 	return rv;
217 }
218 
219 /++
220 Function to extract a DateTime from a binary encoded row.
221 
222 Time/date structures are packed by the server into a byte sub-packet
223 with a leading length byte, and a minimal number of bytes to embody the data.
224 
225 Params: a = slice of a protocol packet beginning at the length byte for a
226             chunk of DateTime data
227 Returns: A populated or default initialized std.datetime.DateTime struct.
228 +/
229 DateTime toDateTime(in ubyte[] a) pure
230 {
231 	enforceEx!MYXProtocol(a.length, "Supplied byte array is zero length");
232 	if (a[0] == 0)
233 		return DateTime();
234 
235 	enforceEx!MYXProtocol(a[0] >= 4, "Supplied ubyte[] is not long enough");
236 	int year    = (a[2] << 8) + a[1];
237 	int month   =  a[3];
238 	int day     =  a[4];
239 	DateTime dt;
240 	if (a[0] == 4)
241 	{
242 		dt = DateTime(year, month, day);
243 	}
244 	else
245 	{
246 		enforceEx!MYXProtocol(a[0] >= 7, "Supplied ubyte[] is not long enough");
247 		int hour    = a[5];
248 		int minute  = a[6];
249 		int second  = a[7];
250 		dt = DateTime(year, month, day, hour, minute, second);
251 	}
252 	return dt;
253 }
254 
255 /++
256 Function to extract a DateTime from a text encoded column value.
257 
258 Text representations of a DateTime are as in 2011-11-11 12:20:02
259 
260 Params: s = A string representation of the time difference.
261 Returns: A populated or default initialized std.datetime.DateTime struct.
262 +/
263 DateTime toDateTime(string s)
264 {
265 	int year = parse!(ushort)(s);
266 	munch(s, "-");
267 	int month = parse!(ubyte)(s);
268 	munch(s, "-");
269 	int day = parse!(ubyte)(s);
270 	munch(s, " ");
271 	int hour = parse!(ubyte)(s);
272 	munch(s, ":");
273 	int minute = parse!(ubyte)(s);
274 	munch(s, ":");
275 	int second = parse!(ubyte)(s);
276 	return DateTime(year, month, day, hour, minute, second);
277 }
278 
279 /++
280 Function to extract a DateTime from a ulong.
281 
282 This is used to support the TimeStamp  struct.
283 
284 Params: x = A ulong e.g. 20111111122002UL.
285 Returns: A populated std.datetime.DateTime struct.
286 +/
287 DateTime toDateTime(ulong x) pure
288 {
289 	int second = cast(int) x%100;
290 	x /= 100;
291 	int minute = cast(int) x%100;
292 	x /= 100;
293 	int hour   = cast(int) x%100;
294 	x /= 100;
295 	int day    = cast(int) x%100;
296 	x /= 100;
297 	int month  = cast(int) x%100;
298 	x /= 100;
299 	int year   = cast(int) x%10000;
300 	// 2038-01-19 03:14:07
301 	enforceEx!MYXProtocol(year >= 1970 &&  year < 2039, "Date/time out of range for 2 bit timestamp");
302 	enforceEx!MYXProtocol(year == 2038 && (month > 1 || day > 19 || hour > 3 || minute > 14 || second > 7),
303 			"Date/time out of range for 2 bit timestamp");
304 	return DateTime(year, month, day, hour, minute, second);
305 }
306 
307 /++
308 Function to pack a DateTime into a binary encoding for transmission to the server.
309 
310 Time/date structures are packed into a string of bytes with a leading length byte,
311 and a minimal number of bytes to embody the data.
312 
313 Params: dt = std.datetime.DateTime struct.
314 Returns: Packed ubyte[].
315 +/
316 ubyte[] pack(in DateTime dt) pure nothrow
317 {
318 	uint len = 1;
319 	if (dt.year || dt.month || dt.day) len = 5;
320 	if (dt.hour || dt.minute|| dt.second) len = 8;
321 	ubyte[] rv;
322 	rv.length = len;
323 	rv[0] =  cast(ubyte)(rv.length - 1); // num bytes
324 	if(len >= 5)
325 	{
326 		rv[1] = cast(ubyte) ( dt.year       & 0xff);
327 		rv[2] = cast(ubyte) ((dt.year >> 8) & 0xff);
328 		rv[3] = cast(ubyte)   dt.month;
329 		rv[4] = cast(ubyte)   dt.day;
330 	}
331 	if(len == 8)
332 	{
333 		rv[5] = cast(ubyte) dt.hour;
334 		rv[6] = cast(ubyte) dt.minute;
335 		rv[7] = cast(ubyte) dt.second;
336 	}
337 	return rv;
338 }
339 
340 
341 T consume(T)(MySQLSocket conn) pure {
342 	ubyte[T.sizeof] buffer;
343 	conn.read(buffer);
344 	ubyte[] rng = buffer;
345 	return consume!T(rng);
346 }
347 
348 string consume(T:string, ubyte N=T.sizeof)(ref ubyte[] packet) pure
349 {
350 	return packet.consume!string(N);
351 }
352 
353 string consume(T:string)(ref ubyte[] packet, size_t N) pure
354 in
355 {
356 	assert(packet.length >= N);
357 }
358 body
359 {
360 	return cast(string)packet.consume(N);
361 }
362 
363 /// Returns N number of bytes from the packet and advances the array
364 ubyte[] consume()(ref ubyte[] packet, size_t N) pure nothrow
365 in
366 {
367 	assert(packet.length >= N);
368 }
369 body
370 {
371 	auto result = packet[0..N];
372 	packet = packet[N..$];
373 	return result;
374 }
375 
376 T decode(T:ulong)(in ubyte[] packet, size_t n) pure nothrow
377 {
378 	switch(n)
379 	{
380 		case 8: return packet.decode!(T, 8)();
381 		case 4: return packet.decode!(T, 4)();
382 		case 3: return packet.decode!(T, 3)();
383 		case 2: return packet.decode!(T, 2)();
384 		case 1: return packet.decode!(T, 1)();
385 		default: assert(0);
386 	}
387 }
388 
389 T consume(T)(ref ubyte[] packet, int n) pure nothrow
390 if(isIntegral!T)
391 {
392 	switch(n)
393 	{
394 		case 8: return packet.consume!(T, 8)();
395 		case 4: return packet.consume!(T, 4)();
396 		case 3: return packet.consume!(T, 3)();
397 		case 2: return packet.consume!(T, 2)();
398 		case 1: return packet.consume!(T, 1)();
399 		default: assert(0);
400 	}
401 }
402 
403 TimeOfDay consume(T:TimeOfDay, ubyte N=T.sizeof)(ref ubyte[] packet) pure
404 in
405 {
406 	static assert(N == T.sizeof);
407 }
408 body
409 {
410 	enforceEx!MYXProtocol(packet.length, "Supplied byte array is zero length");
411 	uint length = packet.front;
412 	enforceEx!MYXProtocol(length == 0 || length == 5 || length == 8 || length == 12, "Bad Time length in binary row.");
413 	enforceEx!MYXProtocol(length >= 8, "Time column value is not in a time-of-day format");
414 
415 	packet.popFront(); // length
416 	auto bytes = packet.consume(length);
417 
418 	// TODO: What are the fields in between!?! Blank Date?
419 	TimeOfDay tod;
420 	tod.hour    = bytes[5];
421 	tod.minute  = bytes[6];
422 	tod.second  = bytes[7];
423 	return tod;
424 }
425 
426 Date consume(T:Date, ubyte N=T.sizeof)(ref ubyte[] packet) pure
427 in
428 {
429 	static assert(N == T.sizeof);
430 }
431 body
432 {
433 	return toDate(packet.consume(5));
434 }
435 
436 DateTime consume(T:DateTime, ubyte N=T.sizeof)(ref ubyte[] packet) pure
437 in
438 {
439 	assert(packet.length);
440 	assert(N == T.sizeof);
441 }
442 body
443 {
444 	auto numBytes = packet.consume!ubyte();
445 	if(numBytes == 0)
446 		return DateTime();
447 
448 	enforceEx!MYXProtocol(numBytes >= 4, "Supplied packet is not large enough to store DateTime");
449 
450 	int year    = packet.consume!ushort();
451 	int month   = packet.consume!ubyte();
452 	int day     = packet.consume!ubyte();
453 	int hour    = 0;
454 	int minute  = 0;
455 	int second  = 0;
456 	if(numBytes > 4)
457 	{
458 		enforceEx!MYXProtocol(numBytes >= 7, "Supplied packet is not large enough to store a DateTime with TimeOfDay");
459 		hour    = packet.consume!ubyte();
460 		minute  = packet.consume!ubyte();
461 		second  = packet.consume!ubyte();
462 	}
463 	return DateTime(year, month, day, hour, minute, second);
464 }
465 
466 
467 @property bool hasEnoughBytes(T, ubyte N=T.sizeof)(in ubyte[] packet) pure
468 in
469 {
470 	static assert(T.sizeof >= N, T.stringof~" not large enough to store "~to!string(N)~" bytes");
471 }
472 body
473 {
474 	return packet.length >= N;
475 }
476 
477 T decode(T, ubyte N=T.sizeof)(in ubyte[] packet) pure nothrow
478 if(isIntegral!T)
479 in
480 {
481 	static assert(N == 1 || N == 2 || N == 3 || N == 4 || N == 8, "Cannot decode integral value. Invalid size: "~N.stringof);
482 	static assert(T.sizeof >= N, T.stringof~" not large enough to store "~to!string(N)~" bytes");
483 	assert(packet.hasEnoughBytes!(T,N), "packet not long enough to contain all bytes needed for "~T.stringof);
484 }
485 body
486 {
487 	T value = 0;
488 	static if(N == 8) // 64 bit
489 	{
490 		value |= cast(T)(packet[7]) << (8*7);
491 		value |= cast(T)(packet[6]) << (8*6);
492 		value |= cast(T)(packet[5]) << (8*5);
493 		value |= cast(T)(packet[4]) << (8*4);
494 	}
495 	static if(N >= 4) // 32 bit
496 	{
497 		value |= cast(T)(packet[3]) << (8*3);
498 	}
499 	static if(N >= 3) // 24 bit
500 	{
501 		value |= cast(T)(packet[2]) << (8*2);
502 	}
503 	static if(N >= 2) // 16 bit
504 	{
505 		value |= cast(T)(packet[1]) << (8*1);
506 	}
507 	static if(N >= 1) // 8 bit
508 	{
509 		value |= cast(T)(packet[0]) << (8*0);
510 	}
511 	return value;
512 }
513 
514 T consume(T, ubyte N=T.sizeof)(ref ubyte[] packet) pure nothrow
515 if(isIntegral!T)
516 in
517 {
518 	static assert(N == 1 || N == 2 || N == 3 || N == 4 || N == 8, "Cannot consume integral value. Invalid size: "~N.stringof);
519 	static assert(T.sizeof >= N, T.stringof~" not large enough to store "~to!string(N)~" bytes");
520 	assert(packet.hasEnoughBytes!(T,N), "packet not long enough to contain all bytes needed for "~T.stringof);
521 }
522 body
523 {
524 	// The uncommented line triggers a template deduction error,
525 	// so we need to store a temporary first
526 	// could the problem be method chaining?
527 	//return packet.consume(N).decode!(T, N)();
528 	auto bytes = packet.consume(N);
529 	return bytes.decode!(T, N)();
530 }
531 
532 
533 T myto(T)(string value)
534 {
535 	static if(is(T == DateTime))
536 		return toDateTime(value);
537 	else static if(is(T == Date))
538 		return toDate(value);
539 	else static if(is(T == TimeOfDay))
540 		return toTimeOfDay(value);
541 	else
542 		return to!T(value);
543 }
544 
545 T decode(T, ubyte N=T.sizeof)(in ubyte[] packet) pure nothrow
546 if(isFloatingPoint!T)
547 in
548 {
549 	static assert((is(T == float) && N == float.sizeof)
550 			|| is(T == double) && N == double.sizeof);
551 }
552 body
553 {
554 	T result = 0;
555 	(cast(ubyte*)&result)[0..T.sizeof] = packet[0..T.sizeof];
556 	return result;
557 }
558 
559 T consume(T, ubyte N=T.sizeof)(ref ubyte[] packet) pure nothrow
560 if(isFloatingPoint!T)
561 in
562 {
563 	static assert((is(T == float) && N == float.sizeof)
564 			|| is(T == double) && N == double.sizeof);
565 }
566 body
567 {
568 	return packet.consume(T.sizeof).decode!T();
569 }
570 
571 
572 SQLValue consumeBinaryValueIfComplete(T, int N=T.sizeof)(ref ubyte[] packet, bool unsigned)
573 {
574 	SQLValue result;
575 	
576 	// Length of DateTime packet is NOT DateTime.sizeof, it can be 1, 5 or 8 bytes
577 	static if(is(T==DateTime))
578 		result.isIncomplete = packet.length < 1;
579 	else
580 		result.isIncomplete = packet.length < N;
581 
582 	// isNull should have been handled by the caller as the binary format uses a
583 	// null bitmap, and we don't have access to that information at this point
584 	assert(!result.isNull);
585 	if(!result.isIncomplete)
586 	{
587 		// only integral types is unsigned
588 		static if(isIntegral!T)
589 		{
590 			if(unsigned)
591 				result.value = packet.consume!(Unsigned!T)();
592 			else
593 				result.value = packet.consume!(Signed!T)();
594 		}
595 		else
596 		{
597 			// TODO: DateTime values etc might be incomplete!
598 			result.value = packet.consume!(T, N)();
599 		}
600 	}
601 	return result;
602 }
603 
604 SQLValue consumeNonBinaryValueIfComplete(T)(ref ubyte[] packet, bool unsigned)
605 {
606 	SQLValue result;
607 	auto lcb = packet.decode!LCB();
608 	result.isIncomplete = lcb.isIncomplete || packet.length < (lcb.value+lcb.totalBytes);
609 	result.isNull = lcb.isNull;
610 	if(!result.isIncomplete)
611 	{
612 		// The packet has all the data we need, so we'll remove the LCB
613 		// and convert the data
614 		packet.skip(lcb.totalBytes);
615 		assert(packet.length >= lcb.value);
616 		auto value = cast(string) packet.consume(cast(size_t)lcb.value);
617 
618 		if(!result.isNull)
619 		{
620 			assert(!result.isIncomplete);
621 			assert(!result.isNull);
622 			static if(isIntegral!T)
623 			{
624 				if(unsigned)
625 					result.value = to!(Unsigned!T)(value);
626 				else
627 					result.value = to!(Signed!T)(value);
628 			}
629 			else
630 			{
631 				static if(isArray!T)
632 				{
633 					// to!() crashes when trying to convert empty strings
634 					// to arrays, so we have this hack to just store any
635 					// empty array in those cases
636 					if(!value.length)
637 						result.value = T.init;
638 					else
639 						result.value = cast(T)value.dup;
640 
641 				}
642 				else
643 				{
644 					// TODO: DateTime values etc might be incomplete!
645 					result.value = myto!T(value);
646 				}
647 			}
648 		}
649 	}
650 	return result;
651 }
652 
653 SQLValue consumeIfComplete(T, int N=T.sizeof)(ref ubyte[] packet, bool binary, bool unsigned)
654 {
655 	return binary
656 		? packet.consumeBinaryValueIfComplete!(T, N)(unsigned)
657 		: packet.consumeNonBinaryValueIfComplete!T(unsigned);
658 }
659 
660 SQLValue consumeIfComplete()(ref ubyte[] packet, SQLType sqlType, bool binary, bool unsigned, ushort charSet)
661 {
662 	switch(sqlType)
663 	{
664 		default: assert(false, "Unsupported SQL type "~to!string(sqlType));
665 		case SQLType.NULL:
666 			SQLValue result;
667 			result.isIncomplete = false;
668 			result.isNull = true;
669 			return result;
670 		case SQLType.TINY:
671 			return packet.consumeIfComplete!byte(binary, unsigned);
672 		case SQLType.SHORT:
673 			return packet.consumeIfComplete!short(binary, unsigned);
674 		case SQLType.INT24:
675 			return packet.consumeIfComplete!(int, 3)(binary, unsigned);
676 		case SQLType.INT:
677 			return packet.consumeIfComplete!int(binary, unsigned);
678 		case SQLType.LONGLONG:
679 			return packet.consumeIfComplete!long(binary, unsigned);
680 		case SQLType.FLOAT:
681 			return packet.consumeIfComplete!float(binary, unsigned);
682 		case SQLType.DOUBLE:
683 		case SQLType.NEWDECIMAL:
684 			return packet.consumeIfComplete!double(binary, unsigned);
685 		case SQLType.TIMESTAMP:
686 			return packet.consumeIfComplete!DateTime(binary, unsigned);
687 		case SQLType.TIME:
688 			return packet.consumeIfComplete!TimeOfDay(binary, unsigned);
689 		case SQLType.YEAR:
690 			return packet.consumeIfComplete!ushort(binary, unsigned);
691 		case SQLType.DATE:
692 			return packet.consumeIfComplete!Date(binary, unsigned);
693 		case SQLType.DATETIME:
694 			return packet.consumeIfComplete!DateTime(binary, unsigned);
695 		case SQLType.VARCHAR:
696 		case SQLType.ENUM:
697 		case SQLType.SET:
698 		case SQLType.VARSTRING:
699 		case SQLType.STRING:
700 			return packet.consumeIfComplete!string(false, unsigned);
701 		case SQLType.TINYBLOB:
702 		case SQLType.MEDIUMBLOB:
703 		case SQLType.BLOB:
704 		case SQLType.LONGBLOB:
705 		case SQLType.BIT:  // Yes, BIT. See note below.
706 
707 			// TODO: This line should work. Why doesn't it?
708 			//return packet.consumeIfComplete!(ubyte[])(binary, unsigned);
709 
710 			auto lcb = packet.consumeIfComplete!LCB();
711 			assert(!lcb.isIncomplete);
712 			SQLValue result;
713 			result.isIncomplete = false;
714 			result.isNull = lcb.isNull;
715 			if(result.isNull)
716 			{
717 				// TODO: consumeIfComplete!LCB should be adjusted to do
718 				//       this itself, but not until I'm certain that nothing
719 				//       is reliant on the current behavior.
720 				packet.popFront(); // LCB length
721 			}
722 			else
723 			{
724 				auto data = packet.consume(cast(size_t)lcb.value);
725 				if(charSet == 0x3F) // CharacterSet == binary
726 					result.value = data; // BLOB-ish
727 				else
728 					result.value = cast(string)data; // TEXT-ish
729 			}
730 			
731 			// Type BIT is treated as a length coded binary (like a BLOB or VARCHAR),
732 			// not like an integral type. So convert the binary data to a bool.
733 			// See: http://dev.mysql.com/doc/internals/en/binary-protocol-value.html
734 			if(sqlType == SQLType.BIT)
735 			{
736 				enforceEx!MYXProtocol(result.value.length == 1,
737 					"Expected BIT to arrive as an LCB with length 1, but got length "~to!string(result.value.length));
738 				
739 				result.value = result.value[0] == 1;
740 			}
741 			
742 			return result;
743 	}
744 }
745 
746 /++
747 Extract number of bytes used for this LCB
748 
749 Returns the number of bytes required to store this LCB
750 
751 See_Also: http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Elements
752 
753 Returns: 0 if it's a null value, or number of bytes in other cases
754 +/
755 byte getNumLCBBytes(in ubyte lcbHeader) pure nothrow
756 {
757 	switch(lcbHeader)
758 	{
759 		case 251: return 0; // null
760 		case 0: .. case 250: return 1; // 8-bit
761 		case 252: return 2;  // 16-bit
762 		case 253: return 3;  // 24-bit
763 		case 254: return 8;  // 64-bit
764 
765 		case 255:
766 		default:
767 			assert(0);
768 	}
769 	assert(0);
770 }
771 
772 
773 /++
774 Decodes a Length Coded Binary from a packet
775 
776 See_Also: struct LCB
777 
778 Parameters:
779 packet = A packet that starts with a LCB. The bytes is popped off
780          iff the packet is complete. See LCB.
781 
782 Returns: A decoded LCB value
783 +/
784 T consumeIfComplete(T:LCB)(ref ubyte[] packet) pure nothrow
785 in
786 {
787 	assert(packet.length >= 1, "packet has to include at least the LCB length byte");
788 }
789 body
790 {
791 	auto lcb = packet.decodeLCBHeader();
792 	if(lcb.isNull || lcb.isIncomplete)
793 		return lcb;
794 
795 	if(lcb.numBytes > 1)
796 	{
797 		// We know it's complete, so we have to start consuming the LCB
798 		// Single byte values doesn't have a length
799 		packet.popFront(); // LCB length
800 	}
801 
802 	assert(packet.length >= lcb.numBytes);
803 
804 	lcb.value = packet.consume!ulong(lcb.numBytes);
805 	return lcb;
806 }
807 
808 LCB decodeLCBHeader(in ubyte[] packet) pure nothrow
809 in
810 {
811 	assert(packet.length >= 1, "packet has to include at least the LCB length byte");
812 }
813 body
814 {
815 	LCB lcb;
816 	lcb.numBytes = getNumLCBBytes(packet.front);
817 	if(lcb.numBytes == 0)
818 	{
819 		lcb.isNull = true;
820 		return lcb;
821 	}
822 
823 	assert(!lcb.isNull);
824 	// -1 for LCB length as we haven't popped it off yet
825 	lcb.isIncomplete = (lcb.numBytes > 1) && (packet.length-1 < lcb.numBytes);
826 	if(lcb.isIncomplete)
827 	{
828 		// Not enough bytes to store data. We don't remove any data, and expect
829 		// the caller to check isIncomplete and take action to fetch more data
830 		// and call this method at a later time
831 		return lcb;
832 	}
833 
834 	assert(!lcb.isIncomplete);
835 	return lcb;
836 }
837 
838 /++
839 Decodes a Length Coded Binary from a packet
840 
841 See_Also: struct LCB
842 
843 Parameters:
844  packet = A packet that starts with a LCB. See LCB.
845 
846 Returns: A decoded LCB value
847 +/
848 LCB decode(T:LCB)(in ubyte[] packet) pure nothrow
849 in
850 {
851 	assert(packet.length >= 1, "packet has to include at least the LCB length byte");
852 }
853 body
854 {
855 	auto lcb = packet.decodeLCBHeader();
856 	if(lcb.isNull || lcb.isIncomplete)
857 		return lcb;
858 	assert(packet.length >= lcb.totalBytes);
859 
860 	if(lcb.numBytes == 0)
861 		lcb.value = 0;
862 	else if(lcb.numBytes == 1)
863 		lcb.value = packet.decode!ulong(lcb.numBytes);
864 	else
865 	{
866 		// Skip the throwaway byte that indicated "at least 2 more bytes coming"
867 		lcb.value = packet[1..$].decode!ulong(lcb.numBytes);
868 	}
869 
870 	return lcb;
871 }
872 
873 /++
874 Parse Length Coded String
875 
876 See_Also: http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Elements
877 +/
878 string consume(T:LCS)(ref ubyte[] packet) pure
879 in
880 {
881 	assert(packet.length >= 1, "LCS packet needs to store at least the LCB header");
882 }
883 body
884 {
885 	auto lcb = packet.consumeIfComplete!LCB();
886 	assert(!lcb.isIncomplete);
887 	if(lcb.isNull)
888 		return null;
889 	enforceEx!MYXProtocol(lcb.value <= uint.max, "Protocol Length Coded String is too long");
890 	return cast(string)packet.consume(cast(size_t)lcb.value).idup;
891 }
892 
893 /++
894 Skips over n items, advances the array, and return the newly advanced array
895 to allow method chaining.
896 +/
897 T[] skip(T)(ref T[] array, size_t n) pure nothrow
898 in
899 {
900 	assert(n <= array.length);
901 }
902 body
903 {
904 	array = array[n..$];
905 	return array;
906 }
907 
908 /++
909 Converts a value into a sequence of bytes and fills the supplied array
910 
911 Parameters:
912 IsInt24 = If only the most significant 3 bytes from the value should be used
913 value = The value to add to array
914 array = The array we should add the values for. It has to be large enough,
915         and the values are packed starting index 0
916 +/
917 void packInto(T, bool IsInt24 = false)(T value, ubyte[] array) pure nothrow
918 in
919 {
920 	static if(IsInt24)
921 		assert(array.length >= 3);
922 	else
923 		assert(array.length >= T.sizeof, "Not enough space to unpack "~T.stringof);
924 }
925 body
926 {
927 	static if(T.sizeof >= 1)
928 	{
929 		array[0] = cast(ubyte) (value >> 8*0) & 0xff;
930 	}
931 	static if(T.sizeof >= 2)
932 	{
933 		array[1] = cast(ubyte) (value >> 8*1) & 0xff;
934 	}
935 	static if(!IsInt24)
936 	{
937 		static if(T.sizeof >= 4)
938 		{
939 			array[2] = cast(ubyte) (value >> 8*2) & 0xff;
940 			array[3] = cast(ubyte) (value >> 8*3) & 0xff;
941 		}
942 		static if(T.sizeof >= 8)
943 		{
944 			array[4] = cast(ubyte) (value >> 8*4) & 0xff;
945 			array[5] = cast(ubyte) (value >> 8*5) & 0xff;
946 			array[6] = cast(ubyte) (value >> 8*6) & 0xff;
947 			array[7] = cast(ubyte) (value >> 8*7) & 0xff;
948 		}
949 	}
950 	else
951 	{
952 		array[2] = cast(ubyte) (value >> 8*2) & 0xff;
953 	}
954 }
955 
956 ubyte[] packLength(size_t l, out size_t offset) pure nothrow
957 out(result)
958 {
959 	assert(result.length >= 1);
960 }
961 body
962 {
963 	ubyte[] t;
964 	if (!l)
965 	{
966 		t.length = 1;
967 		t[0] = 0;
968 	}
969 	else if (l <= 250)
970 	{
971 		t.length = 1+l;
972 		t[0] = cast(ubyte) l;
973 		offset = 1;
974 	}
975 	else if (l <= 0xffff) // 16-bit
976 	{
977 		t.length = 3+l;
978 		t[0] = 252;
979 		packInto(cast(ushort)l, t[1..3]);
980 		offset = 3;
981 	}
982 	else if (l < 0xffffff) // 24-bit
983 	{
984 		t.length = 4+l;
985 		t[0] = 253;
986 		packInto!(uint, true)(cast(uint)l, t[1..4]);
987 		offset = 4;
988 	}
989 	else // 64-bit
990 	{
991 		ulong u = cast(ulong) l;
992 		t.length = 9+l;
993 		t[0] = 254;
994 		u.packInto(t[1..9]);
995 		offset = 9;
996 	}
997 	return t;
998 }
999 
1000 ubyte[] packLCS(void[] a) pure nothrow
1001 {
1002 	size_t offset;
1003 	ubyte[] t = packLength(a.length, offset);
1004 	if (t[0])
1005 		t[offset..$] = (cast(ubyte[]) a)[0..$];
1006 	return t;
1007 }
1008 
1009 
1010 debug(MYSQL_INTEGRATION_TESTS)
1011 unittest
1012 {
1013 	static void testLCB(string parseLCBFunc)(bool shouldConsume)
1014 	{
1015 		ubyte[] buf = [ 0xde, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x01, 0x00 ];
1016 		ubyte[] bufCopy;
1017 		
1018 		bufCopy = buf;
1019 		LCB lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1020 		assert(lcb.value == 0xde && !lcb.isNull && lcb.totalBytes == 1);
1021 		assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1022 
1023 		buf[0] = 251;
1024 		bufCopy = buf;
1025 		lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1026 		assert(lcb.value == 0 && lcb.isNull && lcb.totalBytes == 1);
1027 		//TODO: This test seems to fail for consumeIfComplete, need to investigate.
1028 		//      Don't know if fixing it might cause a problem, or if I simple misunderstood
1029 		//      the function's intent.
1030 		if(parseLCBFunc != "consumeIfComplete")
1031 			assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1032 
1033 		buf[0] = 252;
1034 		bufCopy = buf;
1035 		lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1036 		assert(lcb.value == 0xbbcc && !lcb.isNull && lcb.totalBytes == 3);
1037 		assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1038 
1039 		buf[0] = 253;
1040 		bufCopy = buf;
1041 		lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1042 		assert(lcb.value == 0xaabbcc && !lcb.isNull && lcb.totalBytes == 4);
1043 		assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1044 
1045 		buf[0] = 254;
1046 		bufCopy = buf;
1047 		lcb = mixin(parseLCBFunc~"!LCB(bufCopy)");
1048 		assert(lcb.value == 0x5566778899aabbcc && !lcb.isNull && lcb.totalBytes == 9);
1049 		assert(bufCopy.length == buf.length - (shouldConsume? lcb.totalBytes : 0));
1050 	}
1051 	
1052 	//TODO: Merge 'consumeIfComplete(T:LCB)' and 'decode(T:LCB)', they do
1053 	//      basically the same thing, only one consumes input and the other
1054 	//      doesn't. Just want a better idea of where/how/why they're both
1055 	//      used, and maybe more tests, before I go messing with them.
1056 	testLCB!"consumeIfComplete"(true);
1057 	testLCB!"decode"(false);
1058 }
1059 
1060 debug(MYSQL_INTEGRATION_TESTS)
1061 unittest
1062 {
1063 	ubyte[] buf;
1064 	ubyte[] bufCopy;
1065 
1066 	buf.length = 0x2000200;
1067 	buf[] = '\x01';
1068 	buf[0] = 250;
1069 	buf[1] = '<';
1070 	buf[249] = '!';
1071 	buf[250] = '>';
1072 	bufCopy = buf;
1073 	string x = consume!LCS(bufCopy);
1074 	assert(x.length == 250 && x[0] == '<' && x[249] == '>');
1075 
1076 	buf[] = '\x01';
1077 	buf[0] = 252;
1078 	buf[1] = 0xff;
1079 	buf[2] = 0xff;
1080 	buf[3] = '<';
1081 	buf[0x10000] = '*';
1082 	buf[0x10001] = '>';
1083 	bufCopy = buf;
1084 	x = consume!LCS(bufCopy);
1085 	assert(x.length == 0xffff && x[0] == '<' && x[0xfffe] == '>');
1086 
1087 	buf[] = '\x01';
1088 	buf[0] = 253;
1089 	buf[1] = 0xff;
1090 	buf[2] = 0xff;
1091 	buf[3] = 0xff;
1092 	buf[4] = '<';
1093 	buf[0x1000001] = '*';
1094 	buf[0x1000002] = '>';
1095 	bufCopy = buf;
1096 	x = consume!LCS(bufCopy);
1097 	assert(x.length == 0xffffff && x[0] == '<' && x[0xfffffe] == '>');
1098 
1099 	buf[] = '\x01';
1100 	buf[0] = 254;
1101 	buf[1] = 0xff;
1102 	buf[2] = 0x00;
1103 	buf[3] = 0x00;
1104 	buf[4] = 0x02;
1105 	buf[5] = 0x00;
1106 	buf[6] = 0x00;
1107 	buf[7] = 0x00;
1108 	buf[8] = 0x00;
1109 	buf[9] = '<';
1110 	buf[0x2000106] = '!';
1111 	buf[0x2000107] = '>';
1112 	bufCopy = buf;
1113 	x = consume!LCS(bufCopy);
1114 	assert(x.length == 0x20000ff && x[0] == '<' && x[0x20000fe] == '>');
1115 }
1116 
1117 /// Set packet length and number. It's important that the length of packet has
1118 /// already been set to the final state as its length is used
1119 void setPacketHeader(ref ubyte[] packet, ubyte packetNumber) pure nothrow
1120 in
1121 {
1122 	// packet should include header, and possibly data
1123 	assert(packet.length >= 4);
1124 }
1125 body
1126 {
1127 	auto dataLength = packet.length - 4; // don't include header in calculated size
1128 	assert(dataLength <= uint.max);
1129 	packet.setPacketHeader(packetNumber, cast(uint)dataLength);
1130 }
1131 
1132 void setPacketHeader(ref ubyte[] packet, ubyte packetNumber, uint dataLength) pure nothrow
1133 in
1134 {
1135 	// packet should include header
1136 	assert(packet.length >= 4);
1137 	// Length is always a 24-bit int
1138 	assert(dataLength <= 0xffff_ffff_ffff);
1139 }
1140 body
1141 {
1142 	dataLength.packInto!(uint, true)(packet);
1143 	packet[3] = packetNumber;
1144 }