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