1 /*
2  * Copyright (C) 2019, HuntLabs
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 module hunt.database.driver.postgresql.impl.codec.DataTypeCodec;
19 
20 import hunt.database.driver.postgresql.impl.codec.DataType;
21 import hunt.database.driver.postgresql.impl.codec.DataTypeDesc;
22 
23 import hunt.database.base.Tuple;
24 import hunt.database.base.Numeric;
25 // import hunt.database.driver.postgresql.data.*;
26 // import hunt.database.driver.postgresql.impl.util.UTF8StringEndDetector;
27 // import io.vertx.core.buffer.Buffer;
28 // import io.vertx.core.json.JsonArray;
29 // import io.vertx.core.json.JsonObject;
30 // import io.vertx.core.logging.Logger;
31 // import io.vertx.core.logging.LoggerFactory;
32 
33 // import java.io.IOException;
34 // 
35 // import java.time.*;
36 // import java.time.format.DateTimeFormatterBuilder;
37 // import java.time.temporal.ChronoField;
38 // import java.time.temporal.ChronoUnit;
39 // import java.util.ArrayList;
40 // import hunt.collection.Arrays;
41 // import hunt.collection.List;
42 // import java.util.UUID;
43 // import java.util.function.IntFunction;
44 // import java.util.stream.Collectors;
45 
46 // import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
47 // import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME;
48 // import static java.util.concurrent.TimeUnit.*;
49 
50 import hunt.Byte;
51 import hunt.Exceptions;
52 import hunt.logging;
53 import hunt.net.buffer.ByteBuf;
54 import hunt.net.buffer.Unpooled;
55 import hunt.net.Exceptions;
56 import hunt.String;
57 import hunt.text.Charset;
58 
59 import std.algorithm;
60 import std.array;
61 import std.ascii;
62 import std.concurrency : initOnce;
63 import std.conv;
64 import std.variant;
65 
66 /**
67  * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
68  * @author <a href="mailto:emad.albloushi@gmail.com">Emad Alblueshi</a>
69  */
70 class DataTypeCodec {
71 
72     // private static final String[] empty_string_array = new String[0];
73     // private static final LocalDate[] empty_local_date_array = new LocalDate[0];
74     // private static final LocalTime[] empty_local_time_array = new LocalTime[0];
75     // private static final OffsetTime[] empty_offset_time_array = new OffsetTime[0];
76     // private static final LocalDateTime[] empty_local_date_time_array = new LocalDateTime[0];
77     // private static final OffsetDateTime[] empty_offset_date_time_array = new OffsetDateTime[0];
78     // private static final Buffer[] empty_buffer_array = new Buffer[0];
79     // private static final UUID[] empty_uuid_array = new UUID[0];
80     // private static final Object[] empty_json_array = new Object[0];
81     // private static final Numeric[] empty_numeric_array = new Numeric[0];
82     // private static final Point[] empty_point_array = new Point[0];
83     // private static final Line[] empty_line_array = new Line[0];
84     // private static final LineSegment[] empty_lseg_array = new LineSegment[0];
85     // private static final Box[] empty_box_array = new Box[0];
86     // private static final Path[] empty_path_array = new Path[0];
87     // private static final Polygon[] empty_polygon_array = new Polygon[0];
88     // private static final Circle[] empty_circle_array = new Circle[0];
89     // private static final Interval[] empty_interval_array = new Interval[0];
90     // private static final bool[] empty_boolean_array = new bool[0];
91     // private static final Integer[] empty_integer_array = new Integer[0];
92     // private static final Short[] empty_short_array = new Short[0];
93     // private static final Long[] empty_long_array = new Long[0];
94     // private static final Float[] empty_float_array = new Float[0];
95     // private static final Double[] empty_double_array = new Double[0];
96     // private static final LocalDate LOCAL_DATE_EPOCH = LocalDate.of(2000, 1, 1);
97     // private static final LocalDateTime LOCAL_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
98     // private static final OffsetDateTime OFFSET_DATE_TIME_EPOCH = LocalDateTime.of(2000, 1, 1, 0, 0, 0).atOffset(ZoneOffset.UTC);
99 
100     // Sentinel used when an object is refused by the data type
101     static Object REFUSED_SENTINEL() {
102         __gshared Object inst;
103         return initOnce!inst(new Object());
104     }
105 
106     // private static final IntFunction!(bool[]) BOOLEAN_ARRAY_FACTORY = size -> size == 0 ? empty_boolean_array : new bool[size];
107     // private static final IntFunction!(Short[]) SHORT_ARRAY_FACTORY = size -> size == 0 ? empty_short_array : new Short[size];
108     // private static final IntFunction!(Integer[]) INTEGER_ARRAY_FACTORY = size -> size == 0 ? empty_integer_array : new Integer[size];
109     // private static final IntFunction!(Long[]) LONG_ARRAY_FACTORY = size -> size == 0 ? empty_long_array : new Long[size];
110     // private static final IntFunction!(Float[]) FLOAT_ARRAY_FACTORY = size -> size == 0 ? empty_float_array : new Float[size];
111     // private static final IntFunction!(Double[]) DOUBLE_ARRAY_FACTORY = size -> size == 0 ? empty_double_array : new Double[size];
112     // private static final IntFunction!(String[]) STRING_ARRAY_FACTORY = size -> size == 0 ? empty_string_array : new String[size];
113     // private static final IntFunction!(LocalDate[]) LOCALDATE_ARRAY_FACTORY = size -> size == 0 ? empty_local_date_array : new LocalDate[size];
114     // private static final IntFunction!(LocalTime[]) LOCALTIME_ARRAY_FACTORY = size -> size == 0 ? empty_local_time_array : new LocalTime[size];
115     // private static final IntFunction!(OffsetTime[]) OFFSETTIME_ARRAY_FACTORY = size -> size == 0 ? empty_offset_time_array : new OffsetTime[size];
116     // private static final IntFunction!(LocalDateTime[]) LOCALDATETIME_ARRAY_FACTORY = size -> size == 0 ? empty_local_date_time_array : new LocalDateTime[size];
117     // private static final IntFunction!(OffsetDateTime[]) OFFSETDATETIME_ARRAY_FACTORY = size -> size == 0 ? empty_offset_date_time_array : new OffsetDateTime[size];
118     // private static final IntFunction!(Buffer[]) BUFFER_ARRAY_FACTORY =size -> size == 0 ? empty_buffer_array : new Buffer[size];
119     // private static final IntFunction!(UUID[]) UUID_ARRAY_FACTORY = size -> size == 0 ? empty_uuid_array : new UUID[size];
120     // private static final IntFunction!(Object[]) JSON_ARRAY_FACTORY = size -> size == 0 ? empty_json_array : new Object[size];
121     // private static final IntFunction!(Numeric[]) NUMERIC_ARRAY_FACTORY = size -> size == 0 ? empty_numeric_array : new Numeric[size];
122     // private static final IntFunction!(Point[]) POINT_ARRAY_FACTORY = size -> size == 0 ? empty_point_array : new Point[size];
123     // private static final IntFunction!(Line[]) LINE_ARRAY_FACTORY = size -> size == 0 ? empty_line_array : new Line[size];
124     // private static final IntFunction!(LineSegment[]) LSEG_ARRAY_FACTORY = size -> size == 0 ? empty_lseg_array : new LineSegment[size];
125     // private static final IntFunction!(Box[]) BOX_ARRAY_FACTORY = size -> size == 0 ? empty_box_array : new Box[size];
126     // private static final IntFunction!(Path[]) PATH_ARRAY_FACTORY = size -> size == 0 ? empty_path_array : new Path[size];
127     // private static final IntFunction!(Polygon[]) POLYGON_ARRAY_FACTORY = size -> size == 0 ? empty_polygon_array : new Polygon[size];
128     // private static final IntFunction!(Circle[]) CIRCLE_ARRAY_FACTORY = size -> size == 0 ? empty_circle_array : new Circle[size];
129     // private static final IntFunction!(Interval[]) INTERVAL_ARRAY_FACTORY = size -> size == 0 ? empty_interval_array : new Interval[size];
130 
131     // private static final java.time.format.DateTimeFormatter TIMETZ_FORMAT = new DateTimeFormatterBuilder()
132     //     .parseCaseInsensitive()
133     //     .append(ISO_LOCAL_TIME)
134     //     .appendOffset("+HH:mm", "00:00")
135     //     .toFormatter();
136 
137     // private static final java.time.format.DateTimeFormatter TIMESTAMP_FORMAT = new DateTimeFormatterBuilder()
138     //     .parseCaseInsensitive()
139     //     .append(ISO_LOCAL_DATE)
140     //     .appendLiteral(' ')
141     //     .append(ISO_LOCAL_TIME)
142     //     .toFormatter();
143 
144     // private static final java.time.format.DateTimeFormatter TIMESTAMPTZ_FORMAT = new DateTimeFormatterBuilder()
145     //     .append(TIMESTAMP_FORMAT)
146     //     .appendOffset("+HH:mm", "00:00")
147     //     .toFormatter();
148 
149     static void encodeText(DataType id, ref Variant value, ByteBuf buff) {
150         int index = buff.writerIndex();
151         buff.writeInt(0);
152         textEncode(id, value, buff);
153         buff.setInt(index, buff.writerIndex() - index - 4);
154     }
155 
156     private static void textEncode(DataType id, ref Variant value, ByteBuf buff) {
157         switch (id) {
158             case DataType.NUMERIC:
159                 textEncodeNUMERIC(value, buff);
160                 break;
161             case DataType.NUMERIC_ARRAY:
162                 textEncodeNUMERIC_ARRAY(value, buff);
163                 break;
164             case DataType.UNKNOWN:
165                 //default to treating unknown as a string
166                 buff.writeCharSequence(value.toString(), StandardCharsets.UTF_8);
167                 break;
168             default:
169                 warningf("Data type %s(%d) does not support text encoding", id, cast(int)id);
170                 buff.writeCharSequence(value.toString(), StandardCharsets.UTF_8);
171                 break;
172         }
173     }
174 
175     static void encodeBinary(DataType id, ref Variant value, ByteBuf buff) {
176         switch (id) {
177             case DataType.BOOL:
178                 binaryEncodeBOOL(value, buff);
179                 break;
180             case DataType.BOOL_ARRAY:
181                 binaryEncodeArray!(bool)(value, DataType.BOOL, buff);
182                 break;
183             case DataType.INT2:
184                 binaryEncodeINT2(value, buff);
185                 break;
186             case DataType.INT2_ARRAY:
187                 binaryEncodeArray!(short)(value, DataType.INT2, buff);
188                 break;
189             case DataType.INT4:
190                 binaryEncodeINT4(value, buff);
191                 break;
192             case DataType.INT4_ARRAY:
193                 binaryEncodeArray!(int)(value, DataType.INT4, buff);
194                 break;
195             case DataType.INT8:
196                 binaryEncodeINT8(value, buff);
197                 break;
198             case DataType.INT8_ARRAY:
199                 binaryEncodeArray!(long)(value, DataType.INT8, buff);
200                 break;
201             case DataType.FLOAT4:
202                 binaryEncodeFLOAT4(value, buff);
203                 break;
204             case DataType.FLOAT4_ARRAY:
205                 binaryEncodeArray!(float)(value, DataType.FLOAT4, buff);
206                 break;
207             case DataType.FLOAT8:
208                 binaryEncodeFLOAT8(value, buff);
209                 break;
210             case DataType.FLOAT8_ARRAY:
211                 binaryEncodeArray!(double)(value, DataType.FLOAT8, buff);
212                 break;
213             case DataType.CHAR:
214                 binaryEncodeCHAR(value, buff);
215                 break;
216             case DataType.CHAR_ARRAY:
217                 binaryEncodeArray!(string)(value, DataType.CHAR, buff);
218                 break;
219             case DataType.VARCHAR:
220                 binaryEncodeVARCHAR(value, buff);
221                 break;
222             case DataType.VARCHAR_ARRAY:
223                 binaryEncodeArray!(string)(value, DataType.VARCHAR, buff);
224                 break;
225             case DataType.BPCHAR:
226                 binaryEncodeBPCHAR(value, buff);
227                 break;
228             case DataType.BPCHAR_ARRAY:
229                 binaryEncodeArray!(string)(value, DataType.BPCHAR, buff);
230                 break;
231             case DataType.TEXT:
232                 binaryEncodeTEXT(value, buff);
233                 break;
234             case DataType.TEXT_ARRAY:
235                 binaryEncodeArray!(string)(value, DataType.TEXT, buff);
236                 break;
237             case DataType.NAME:
238                 binaryEncodeNAME(value, buff);
239                 break;
240             case DataType.NAME_ARRAY:
241                 binaryEncodeArray!(string)(value, DataType.NAME, buff);
242                 break;
243     //         case DataType.DATE:
244     //             binaryEncodeDATE((LocalDate) value, buff);
245     //             break;
246     //         case DataType.DATE_ARRAY:
247     //             binaryEncodeArray((LocalDate[]) value, DataType.DATE, buff);
248     //             break;
249     //         case DataType.TIME:
250     //             binaryEncodeTIME((LocalTime) value, buff);
251     //             break;
252     //         case DataType.TIME_ARRAY:
253     //             binaryEncodeArray((LocalTime[]) value, DataType.TIME, buff);
254     //             break;
255     //         case DataType.TIMETZ:
256     //             binaryEncodeTIMETZ((OffsetTime) value, buff);
257     //             break;
258     //         case DataType.TIMETZ_ARRAY:
259     //             binaryEncodeArray((OffsetTime[]) value, DataType.TIMETZ, buff);
260     //             break;
261     //         case DataType.TIMESTAMP:
262     //             binaryEncodeTIMESTAMP((LocalDateTime) value, buff);
263     //             break;
264     //         case DataType.TIMESTAMP_ARRAY:
265     //             binaryEncodeArray((LocalDateTime[]) value, DataType.TIMESTAMP, buff);
266     //             break;
267     //         case DataType.TIMESTAMPTZ:
268     //             binaryEncodeTIMESTAMPTZ((OffsetDateTime) value, buff);
269     //             break;
270     //         case DataType.TIMESTAMPTZ_ARRAY:
271     //             binaryEncodeArray((OffsetDateTime[]) value, DataType.TIMESTAMPTZ, buff);
272     //             break;
273             case DataType.BYTEA:
274                 binaryEncodeBYTEA(value, buff);
275                 break;
276             case DataType.BYTEA_ARRAY:
277                 binaryEncodeArray!(byte)(value, DataType.BYTEA, buff);
278                 break;
279     //         case DataType.UUID:
280     //             binaryEncodeUUID((UUID) value, buff);
281     //             break;
282     //         case DataType.UUID_ARRAY:
283     //             binaryEncodeArray((UUID[]) value, DataType.UUID, buff);
284     //             break;
285     //         case DataType.JSON:
286     //             binaryEncodeJSON((Object) value, buff);
287     //             break;
288     //         case DataType.JSON_ARRAY:
289     //             binaryEncodeArray((Object[]) value, DataType.JSON, buff);
290     //             break;
291     //         case DataType.JSONB:
292     //             binaryEncodeJSONB((Object) value, buff);
293     //             break;
294     //         case DataType.JSONB_ARRAY:
295     //             binaryEncodeArray((Object[]) value, DataType.JSONB, buff);
296     //             break;
297     //         case DataType.POINT:
298     //             binaryEncodePoint((Point) value, buff);
299     //             break;
300     //         case DataType.POINT_ARRAY:
301     //             binaryEncodeArray((Point[]) value, DataType.POINT, buff);
302     //             break;
303     //         case DataType.LINE:
304     //             binaryEncodeLine((Line) value, buff);
305     //             break;
306     //         case DataType.LINE_ARRAY:
307     //             binaryEncodeArray((Line[]) value, DataType.LINE, buff);
308     //             break;
309     //         case DataType.LSEG:
310     //             binaryEncodeLseg((LineSegment) value, buff);
311     //             break;
312     //         case DataType.LSEG_ARRAY:
313     //             binaryEncodeArray((LineSegment[]) value, DataType.LSEG, buff);
314     //             break;
315     //         case DataType.BOX:
316     //             binaryEncodeBox((Box) value, buff);
317     //             break;
318     //         case DataType.BOX_ARRAY:
319     //             binaryEncodeArray((Box[]) value, DataType.BOX, buff);
320     //             break;
321     //         case DataType.PATH:
322     //             binaryEncodePath((Path) value, buff);
323     //             break;
324     //         case DataType.PATH_ARRAY:
325     //             binaryEncodeArray((Path[]) value, DataType.PATH, buff);
326     //             break;
327     //         case DataType.POLYGON:
328     //             binaryEncodePolygon((Polygon) value, buff);
329     //             break;
330     //         case DataType.POLYGON_ARRAY:
331     //             binaryEncodeArray((Polygon[]) value, DataType.POLYGON, buff);
332     //             break;
333     //         case DataType.CIRCLE:
334     //             binaryEncodeCircle((Circle) value, buff);
335     //             break;
336     //         case DataType.CIRCLE_ARRAY:
337     //             binaryEncodeArray((Circle[]) value, DataType.CIRCLE, buff);
338     //             break;
339     //         case DataType.INTERVAL:
340     //             binaryEncodeINTERVAL((Interval) value, buff);
341     //             break;
342     //         case DataType.INTERVAL_ARRAY:
343     //             binaryEncodeArray((Interval[]) value, DataType.INTERVAL, buff);
344     //             break;
345             default:
346                 warningf("Data type %s(%d) does not support binary encoding", id, cast(int)id);
347                 defaultEncodeBinary(value, buff);
348                 break;
349         }
350     }
351 
352     // static Variant decodeBinary(DataType id, int index, int len, ByteBuf buff) {
353     //     byte[] buffer = new byte[len];
354     //     buff.getBytes(index, buffer);
355         
356     //     tracef("DataType: %d, data: %(%02X %)", id, buffer);
357     //     implementationMissing(false);
358 
359     //     return new Bytes(buffer);
360     // }
361 
362     static Variant decodeBinary(DataType id, int index, int len, ByteBuf buff) {
363         switch (id) {
364             case DataType.BOOL:
365                 return binaryDecodeBOOL(index, len, buff).Variant();
366             // case DataType.BOOL_ARRAY:
367             //     return binaryDecodeArray(BOOLEAN_ARRAY_FACTORY, DataType.BOOL, index, len, buff).Variant();
368             case DataType.INT2:
369                 return binaryDecodeINT2(index, len, buff).Variant();
370             // case DataType.INT2_ARRAY:
371             //     return binaryDecodeArray(SHORT_ARRAY_FACTORY, DataType.INT2, index, len, buff).Variant();
372             case DataType.INT4:
373                 return binaryDecodeINT4(index, len, buff).Variant();
374     //         case DataType.INT4_ARRAY:
375     //             return binaryDecodeArray(INTEGER_ARRAY_FACTORY, DataType.INT4, index, len, buff).Variant();
376             case DataType.INT8:
377                 return binaryDecodeINT8(index, len, buff).Variant();
378     //         case DataType.INT8_ARRAY:
379     //             return binaryDecodeArray(LONG_ARRAY_FACTORY, DataType.INT8, index, len, buff).Variant();
380             case DataType.FLOAT4:
381                 return binaryDecodeFLOAT4(index, len, buff).Variant();
382     //         case DataType.FLOAT4_ARRAY:
383     //             return binaryDecodeArray(FLOAT_ARRAY_FACTORY, DataType.FLOAT4, index, len, buff).Variant();
384             case DataType.FLOAT8:
385                 return binaryDecodeFLOAT8(index, len, buff).Variant();
386     //         case DataType.FLOAT8_ARRAY:
387     //             return binaryDecodeArray(DOUBLE_ARRAY_FACTORY, DataType.FLOAT8, index, len, buff).Variant();
388             case DataType.CHAR:
389                 return binaryDecodeCHAR(index, len, buff).Variant();
390     //         case DataType.CHAR_ARRAY:
391     //             return binaryDecodeArray(STRING_ARRAY_FACTORY, DataType.CHAR, index, len, buff).Variant();
392             case DataType.VARCHAR:
393                 return binaryDecodeVARCHAR(index, len, buff).Variant();
394     //         case DataType.VARCHAR_ARRAY:
395     //             return binaryDecodeArray(STRING_ARRAY_FACTORY, DataType.VARCHAR, index, len, buff).Variant();
396             case DataType.BPCHAR:
397                 return binaryDecodeBPCHAR(index, len, buff).Variant();
398     //         case DataType.BPCHAR_ARRAY:
399     //             return binaryDecodeArray(STRING_ARRAY_FACTORY, DataType.BPCHAR, index, len, buff).Variant();
400             case DataType.TEXT:
401                 return binaryDecodeTEXT(index, len, buff).Variant();
402     //         case DataType.TEXT_ARRAY:
403     //             return binaryDecodeArray(STRING_ARRAY_FACTORY, DataType.TEXT, index, len, buff).Variant();
404             case DataType.NAME:
405                 return binaryDecodeNAME(index, len, buff).Variant();
406     //         case DataType.NAME_ARRAY:
407     //             return binaryDecodeArray(STRING_ARRAY_FACTORY, DataType.NAME, index, len, buff).Variant();
408     //         case DataType.DATE:
409     //             return binaryDecodeDATE(index, len, buff).Variant();
410     //         case DataType.DATE_ARRAY:
411     //             return binaryDecodeArray(LOCALDATE_ARRAY_FACTORY, DataType.DATE, index, len, buff).Variant();
412     //         case DataType.TIME:
413     //             return binaryDecodeTIME(index, len, buff).Variant();
414     //         case DataType.TIME_ARRAY:
415     //             return binaryDecodeArray(LOCALTIME_ARRAY_FACTORY, DataType.TIME, index, len, buff).Variant();
416     //         case DataType.TIMETZ:
417     //             return binaryDecodeTIMETZ(index, len, buff).Variant();
418     //         case DataType.TIMETZ_ARRAY:
419     //             return binaryDecodeArray(OFFSETTIME_ARRAY_FACTORY, DataType.TIMETZ, index, len, buff).Variant();
420     //         case DataType.TIMESTAMP:
421     //             return binaryDecodeTIMESTAMP(index, len, buff).Variant();
422     //         case DataType.TIMESTAMP_ARRAY:
423     //             return binaryDecodeArray(LOCALDATETIME_ARRAY_FACTORY, DataType.TIMESTAMP, index, len, buff).Variant();
424     //         case DataType.TIMESTAMPTZ:
425     //             return binaryDecodeTIMESTAMPTZ(index, len, buff).Variant();
426     //         case DataType.TIMESTAMPTZ_ARRAY:
427     //             return binaryDecodeArray(OFFSETDATETIME_ARRAY_FACTORY, DataType.TIMESTAMPTZ, index, len, buff).Variant();
428             case DataType.BYTEA:
429                 return binaryDecodeBYTEA(index, len, buff).Variant();
430     //         case DataType.BYTEA_ARRAY:
431     //             return binaryDecodeArray(BUFFER_ARRAY_FACTORY, DataType.BYTEA, index, len, buff).Variant();
432     //         case DataType.UUID:
433     //             return binaryDecodeUUID(index, len, buff).Variant();
434     //         case DataType.UUID_ARRAY:
435     //             return binaryDecodeArray(UUID_ARRAY_FACTORY, DataType.UUID, index, len, buff).Variant();
436     //         case DataType.JSON:
437     //             return binaryDecodeJSON(index, len, buff).Variant();
438     //         case DataType.JSON_ARRAY:
439     //             return binaryDecodeArray(JSON_ARRAY_FACTORY, DataType.JSON, index, len, buff).Variant();
440     //         case DataType.JSONB:
441     //             return binaryDecodeJSONB(index, len, buff).Variant();
442     //         case DataType.JSONB_ARRAY:
443     //             return binaryDecodeArray(JSON_ARRAY_FACTORY, DataType.JSONB, index, len, buff).Variant();
444     //         case DataType.POINT:
445     //             return binaryDecodePoint(index, len, buff).Variant();
446     //         case DataType.POINT_ARRAY:
447     //             return binaryDecodeArray(POINT_ARRAY_FACTORY, DataType.POINT, index, len, buff).Variant();
448     //         case DataType.LINE:
449     //             return binaryDecodeLine(index, len, buff).Variant();
450     //         case DataType.LINE_ARRAY:
451     //             return binaryDecodeArray(LINE_ARRAY_FACTORY, DataType.LINE, index, len, buff).Variant();
452     //         case DataType.LSEG:
453     //             return binaryDecodeLseg(index, len, buff).Variant();
454     //         case DataType.LSEG_ARRAY:
455     //             return binaryDecodeArray(LSEG_ARRAY_FACTORY, DataType.LSEG, index, len, buff).Variant();
456     //         case DataType.BOX:
457     //             return binaryDecodeBox(index, len, buff).Variant();
458     //         case DataType.BOX_ARRAY:
459     //             return binaryDecodeArray(BOX_ARRAY_FACTORY, DataType.BOX, index, len, buff).Variant();
460     //         case DataType.PATH:
461     //             return binaryDecodePath(index, len, buff).Variant();
462     //         case DataType.PATH_ARRAY:
463     //             return binaryDecodeArray(PATH_ARRAY_FACTORY, DataType.PATH, index, len, buff).Variant();
464     //         case DataType.POLYGON:
465     //             return binaryDecodePolygon(index, len, buff).Variant();
466     //         case DataType.POLYGON_ARRAY:
467     //             return binaryDecodeArray(POLYGON_ARRAY_FACTORY, DataType.POLYGON, index, len, buff).Variant();
468     //         case DataType.CIRCLE:
469     //             return binaryDecodeCircle(index, len, buff).Variant();
470     //         case DataType.CIRCLE_ARRAY:
471     //             return binaryDecodeArray(CIRCLE_ARRAY_FACTORY, DataType.CIRCLE, index, len, buff).Variant();
472     //         case DataType.INTERVAL:
473     //             return binaryDecodeINTERVAL(index, len, buff).Variant();
474     //         case DataType.INTERVAL_ARRAY:
475     //             return binaryDecodeArray(INTERVAL_ARRAY_FACTORY, DataType.INTERVAL, index, len, buff).Variant();
476             default:
477                 warningf("Data type %s(%d) does not support binary decoding", id, id);
478                 return Variant(null);
479                 // return defaultDecodeBinary(index, len, buff);
480         }
481     }
482 
483     static Variant decodeText(DataType id, int index, int len, ByteBuf buff) {
484         switch (id) {
485             case DataType.BOOL:
486                 return textDecodeBOOL(index, len, buff).Variant();
487     //         case DataType.BOOL_ARRAY:
488     //             return textDecodeArray(BOOLEAN_ARRAY_FACTORY, DataType.BOOL, index, len, buff).Variant();
489             case DataType.INT2:
490                 return textDecodeINT2(index, len, buff).Variant();
491     //         case DataType.INT2_ARRAY:
492     //             return textDecodeArray(SHORT_ARRAY_FACTORY, DataType.INT2, index, len, buff).Variant();
493             case DataType.INT4:
494                 return textDecodeINT4(index, len, buff).Variant();
495     //         case DataType.INT4_ARRAY:
496     //             return textDecodeArray(INTEGER_ARRAY_FACTORY, DataType.INT4, index, len, buff).Variant();
497             case DataType.INT8:
498                 return textDecodeINT8(index, len, buff).Variant();
499     //         case DataType.INT8_ARRAY:
500     //             return textDecodeArray(LONG_ARRAY_FACTORY, DataType.INT8, index, len, buff).Variant();
501             case DataType.FLOAT4:
502                 return textDecodeFLOAT4(index, len, buff).Variant();
503     //         case DataType.FLOAT4_ARRAY:
504     //             return textDecodeArray(FLOAT_ARRAY_FACTORY, DataType.FLOAT4, index, len, buff).Variant();
505             case DataType.FLOAT8:
506                 return textDecodeFLOAT8(index, len, buff).Variant();
507     //         case DataType.FLOAT8_ARRAY:
508     //             return textDecodeArray(DOUBLE_ARRAY_FACTORY, DataType.FLOAT8, index, len, buff).Variant();
509             case DataType.CHAR:
510                 return textDecodeCHAR(index, len, buff).Variant();
511     //         // case DataType.CHAR_ARRAY:
512     //         //   return textDecodeCHAR_ARRAY(len, buff).Variant();
513             case DataType.VARCHAR:
514                 return textDecodeVARCHAR(index, len, buff).Variant();
515     //         case DataType.VARCHAR_ARRAY:
516     //             return textDecodeArray(STRING_ARRAY_FACTORY, DataType.VARCHAR, index, len, buff).Variant();
517             case DataType.BPCHAR:
518                 return textDecodeBPCHAR(index, len, buff).Variant();
519     //         case DataType.BPCHAR_ARRAY:
520     //             return textDecodeArray(STRING_ARRAY_FACTORY, DataType.BPCHAR, index, len, buff).Variant();
521             case DataType.TEXT:
522                 return textdecodeTEXT(index, len, buff).Variant();
523     //         case DataType.TEXT_ARRAY:
524     //             return textDecodeArray(STRING_ARRAY_FACTORY, DataType.TEXT, index, len, buff).Variant();
525             case DataType.NAME:
526                 return textDecodeNAME(index, len, buff).Variant();
527     //         case DataType.NAME_ARRAY:
528     //             return textDecodeArray(STRING_ARRAY_FACTORY, DataType.NAME, index, len, buff).Variant();
529     //         case DataType.DATE:
530     //             return textDecodeDATE(index, len, buff).Variant();
531     //         case DataType.DATE_ARRAY:
532     //             return textDecodeArray(LOCALDATE_ARRAY_FACTORY, DataType.DATE, index, len, buff).Variant();
533     //         case DataType.TIME:
534     //             return textDecodeTIME(index, len, buff).Variant();
535     //         case DataType.TIME_ARRAY:
536     //             return textDecodeArray(LOCALTIME_ARRAY_FACTORY, DataType.TIME, index, len, buff).Variant();
537     //         case DataType.TIMETZ:
538     //             return textDecodeTIMETZ(index, len, buff).Variant();
539     //         case DataType.TIMETZ_ARRAY:
540     //             return textDecodeArray(OFFSETTIME_ARRAY_FACTORY, DataType.TIMETZ, index, len, buff).Variant();
541     //         case DataType.TIMESTAMP:
542     //             return textDecodeTIMESTAMP(index, len, buff).Variant();
543     //         case DataType.TIMESTAMP_ARRAY:
544     //             return textDecodeArray(LOCALDATETIME_ARRAY_FACTORY, DataType.TIMESTAMP, index, len, buff).Variant();
545     //         case DataType.TIMESTAMPTZ:
546     //             return textDecodeTIMESTAMPTZ(index, len, buff).Variant();
547     //         case DataType.TIMESTAMPTZ_ARRAY:
548     //             return textDecodeArray(OFFSETDATETIME_ARRAY_FACTORY, DataType.TIMESTAMPTZ, index, len, buff).Variant();
549             case DataType.BYTEA:
550                 return textDecodeBYTEA(index, len, buff).Variant();
551     //         case DataType.BYTEA_ARRAY:
552     //             return textDecodeArray(BUFFER_ARRAY_FACTORY, DataType.BYTEA, index, len, buff).Variant();
553     //         case DataType.UUID:
554     //             return textDecodeUUID(index, len, buff).Variant();
555     //         case DataType.UUID_ARRAY:
556     //             return textDecodeArray(UUID_ARRAY_FACTORY, DataType.UUID, index, len, buff).Variant();
557             case DataType.NUMERIC:
558                 return textDecodeNUMERIC(index, len, buff).Variant();
559     //         case DataType.NUMERIC_ARRAY:
560     //             return textDecodeArray(NUMERIC_ARRAY_FACTORY, DataType.NUMERIC, index, len, buff).Variant();
561     //         case DataType.JSON:
562     //             return textDecodeJSON(index, len, buff).Variant();
563     //         case DataType.JSON_ARRAY:
564     //             return textDecodeArray(JSON_ARRAY_FACTORY, DataType.JSON, index, len, buff).Variant();
565     //         case DataType.JSONB:
566     //              return textDecodeJSONB(index, len, buff).Variant();
567     //         case DataType.JSONB_ARRAY:
568     //             return textDecodeArray(JSON_ARRAY_FACTORY, DataType.JSONB, index, len, buff).Variant();
569     //         case DataType.POINT:
570     //             return textDecodePOINT(index, len, buff).Variant();
571     //         case DataType.POINT_ARRAY:
572     //             return textDecodeArray(POINT_ARRAY_FACTORY, DataType.POINT, index, len, buff).Variant();
573     //         case DataType.LINE:
574     //             return textDecodeLine(index, len, buff).Variant();
575     //         case DataType.LINE_ARRAY:
576     //             return textDecodeArray(LINE_ARRAY_FACTORY, DataType.LINE, index, len, buff).Variant();
577     //         case DataType.LSEG:
578     //             return textDecodeLseg(index, len, buff).Variant();
579     //         case DataType.LSEG_ARRAY:
580     //             return textDecodeArray(LSEG_ARRAY_FACTORY, DataType.LSEG, index, len, buff).Variant();
581     //         case DataType.BOX:
582     //             return textDecodeBox(index, len, buff).Variant();
583     //         case DataType.BOX_ARRAY:
584     //             return textDecodeBoxArray(BOX_ARRAY_FACTORY, index, len, buff).Variant();
585     //         case DataType.PATH:
586     //             return textDecodePath(index, len, buff).Variant();
587     //         case DataType.PATH_ARRAY:
588     //             return textDecodeArray(PATH_ARRAY_FACTORY, DataType.PATH, index, len, buff).Variant();
589     //         case DataType.POLYGON:
590     //             return textDecodePolygon(index, len, buff).Variant();
591     //         case DataType.POLYGON_ARRAY:
592     //             return textDecodeArray(POLYGON_ARRAY_FACTORY, DataType.POLYGON, index, len, buff).Variant();
593     //         case DataType.CIRCLE:
594     //             return textDecodeCircle(index, len, buff).Variant();
595     //         case DataType.CIRCLE_ARRAY:
596     //             return textDecodeArray(CIRCLE_ARRAY_FACTORY, DataType.CIRCLE, index, len, buff).Variant();
597     //         case DataType.INTERVAL:
598     //             return textDecodeINTERVAL(index, len, buff).Variant();
599     //         case DataType.INTERVAL_ARRAY:
600     //             return textDecodeArray(INTERVAL_ARRAY_FACTORY, DataType.INTERVAL, index, len, buff).Variant();
601             default:
602                 warningf("Data type %s(%d) does not support text decoding", id, id);
603                 return defaultDecodeText(index, len, buff);
604         }
605     }
606 
607     static bool isNumber(TypeInfo info) {
608         return info == typeid(short) ||
609         info == typeid(ushort) ||
610         info == typeid(int) ||
611         info == typeid(uint) ||
612         info == typeid(long) ||
613         info == typeid(ulong) ||
614         info == typeid(float) ||
615         info == typeid(double) ;
616     }
617 
618     static Variant prepare(DataTypeDesc type, ref Variant value) {
619         TypeInfo valueType = value.type;
620 
621         switch (cast(DataType)type.id) {
622             case DataType.JSON:
623             case DataType.JSONB:
624                 if (!value.hasValue() || valueType == typeid(null) ||
625                         valueType == typeid(string) ||
626                         valueType == typeid(bool) ||
627                         isNumber(valueType)) {  // value instanceof JsonObject || value instanceof JsonArray
628                     return value;
629                 } else {
630                     return REFUSED_SENTINEL.Variant();
631                 }
632 
633             case DataType.UNKNOWN:
634                 if (valueType == typeid(string[])) {
635                     // return Arrays.stream((String[]) value).collect(Collectors.joining(",", "{", "}"));
636                     throw new NotImplementedException();
637                 } else if (!value.hasValue() || valueType == typeid(null) || valueType == typeid(string)) {
638                     return value;
639                 } else {
640                     return REFUSED_SENTINEL.Variant();
641                 }
642 
643             default:
644                 if (!value.hasValue() || valueType == typeid(null) || 
645                         type.decodingType.empty() || type.decodingType.canFind(valueType.toString()))
646                     return value;
647                 else 
648                     return REFUSED_SENTINEL.Variant();
649                 // FIXME: Needing refactor or cleanup -@zxp at 8/28/2019, 3:31:47 PM                    
650                 // 
651                 // Class<?> javaType = type.decodingType;
652                 // return !value.hasValue() || valueType == typeid(null) || javaType.isInstance(value) ? value : REFUSED_SENTINEL;
653         }
654     }
655 
656     private static Variant defaultDecodeText(int index, int len, ByteBuf buff) {
657         // decode unknown text values as text or as an array if it begins with `{`
658         // if (len > 1 && buff.getByte(index) == '{') {
659         //     return textDecodeArray(STRING_ARRAY_FACTORY, DataType.TEXT, index, len, buff);
660         // }
661         // FIXME: Needing refactor or cleanup -@zxp at 8/28/2019, 10:34:26 AM
662         // 
663         implementationMissing(false);
664         return textdecodeTEXT(index, len, buff).Variant();
665     }
666 
667     private static void defaultEncodeBinary(ref Variant value, ByteBuf buff) {
668         // Default to null
669         buff.writeInt(-1);
670     }
671 
672     // private static Object defaultDecodeBinary(int index, int len, ByteBuf buff) {
673     //     // Default to null
674     //     return null;
675     // }
676 
677     private static void binaryEncodeBOOL(ref Variant value, ByteBuf buff) {
678         assert(value.type == typeid(bool));
679         buff.writeBoolean(value.get!(bool));
680     }
681 
682     private static bool binaryDecodeBOOL(int index, int len, ByteBuf buff) {
683         return buff.getBoolean(index);
684     }
685 
686     private static bool textDecodeBOOL(int index, int len, ByteBuf buff) {
687         if(buff.getByte(index) == 't') {
688             return true;
689         } else {
690             return false;
691         }
692     }
693 
694     private static short textDecodeINT2(int index, int len, ByteBuf buff) {
695         return cast(short) DataTypeCodec.decodeDecStringToLong(index, len, buff);
696     }
697 
698     private static short binaryDecodeINT2(int index, int len, ByteBuf buff) {
699         return buff.getShort(index);
700     }
701 
702     private static void binaryEncodeINT2(ref Variant value, ByteBuf buff) {
703         assert(value.type == typeid(short) || value.type == typeid(ushort));
704         buff.writeShort(value.get!(int)());
705     }
706 
707     private static int textDecodeINT4(int index, int len, ByteBuf buff) {
708         return cast(int) decodeDecStringToLong(index, len, buff);
709     }
710 
711     private static int binaryDecodeINT4(int index, int len, ByteBuf buff) {
712         return buff.getInt(index);
713     }
714 
715     private static void binaryEncodeINT4(ref Variant value, ByteBuf buff) {
716         assert(value.type == typeid(int) || value.type == typeid(uint));
717         buff.writeInt(value.get!(int));
718     }
719 
720     private static long textDecodeINT8(int index, int len, ByteBuf buff) {
721         return decodeDecStringToLong(index, len, buff);
722     }
723 
724     private static long binaryDecodeINT8(int index, int len, ByteBuf buff) {
725         return buff.getLong(index);
726     }
727 
728     private static void binaryEncodeINT8(ref Variant value, ByteBuf buff) {
729         assert(value.type == typeid(long) || value.type == typeid(ulong));
730         buff.writeLong(value.get!(long));
731     }
732 
733     private static float textDecodeFLOAT4(int index, int len, ByteBuf buff) {
734         CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
735         return cs.to!float();
736     }
737 
738     private static float binaryDecodeFLOAT4(int index, int len, ByteBuf buff) {
739         return buff.getFloat(index);
740     }
741 
742     private static void binaryEncodeFLOAT4(ref Variant value, ByteBuf buff) {
743         assert(value.type == typeid(float));
744         buff.writeFloat(value.get!(float)());
745     }
746 
747     private static void binaryEncodeFLOAT8(ref Variant value, ByteBuf buff) {
748         assert(value.type == typeid(double));
749         buff.writeDouble(value.get!(double)());
750     }
751 
752     private static double binaryDecodeFLOAT8(int index, int len, ByteBuf buff) {
753         return buff.getDouble(index);
754     }
755 
756     private static double textDecodeFLOAT8(int index, int len, ByteBuf buff) {
757         CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
758         return cs.to!double();
759     }
760 
761     private static double textDecodeNUMERIC(int index, int len, ByteBuf buff) {
762         // Todo optimize that
763         CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
764         // return Numeric.parse(cs.toString());
765         double v = 0;
766         
767         try {
768             v = cs.to!double();
769         } catch (Exception ex) {
770             warningf("Not a number: %s", cs);
771         }
772 
773         return v;
774     }
775 
776     // private static Point textDecodePOINT(int index, int len, ByteBuf buff) {
777     //     // Point representation: (x,y)
778     //     int idx = ++index;
779     //     int s = buff.indexOf(idx, idx + len, (byte) ',');
780     //     int t = s - idx;
781     //     double x = textDecodeFLOAT8(idx, t, buff);
782     //     double y = textDecodeFLOAT8(s + 1, len - t - 3, buff);
783     //     return new Point(x, y);
784     // }
785 
786     // private static Line textDecodeLine(int index, int len, ByteBuf buff) {
787     //     // Line representation: {a,b,c}
788     //     int idxOfFirstSeparator = buff.indexOf(index, index + len, (byte) ',');
789     //     int idxOfLastSeparator = buff.indexOf(index + len, index, (byte) ',');
790 
791     //     int idx = index + 1;
792     //     double a = textDecodeFLOAT8(idx, idxOfFirstSeparator - idx, buff);
793     //     double b = textDecodeFLOAT8(idxOfFirstSeparator + 1, idxOfLastSeparator - idxOfFirstSeparator - 1, buff);
794     //     double c = textDecodeFLOAT8(idxOfLastSeparator + 1, index + len - idxOfLastSeparator - 2, buff);
795     //     return new Line(a, b, c);
796     // }
797 
798     // private static LineSegment textDecodeLseg(int index, int len, ByteBuf buff) {
799     //     // Lseg representation: [p1,p2]
800     //     int idxOfPointsSeparator = buff.indexOf(index, index+len, (byte) ')') + 1;
801     //     int lenOfP1 = idxOfPointsSeparator - index - 1;
802     //     Point p1 = textDecodePOINT(index + 1, lenOfP1, buff);
803     //     Point p2 = textDecodePOINT(idxOfPointsSeparator + 1, len - lenOfP1 - 3, buff);
804     //     return new LineSegment(p1, p2);
805     // }
806 
807     // private static Box textDecodeBox(int index, int len, ByteBuf buff) {
808     //     // Box representation: p1,p2
809     //     int idxOfPointsSeparator = buff.indexOf(index, index+len, (byte) ')') + 1;
810     //     int lenOfUpperRightCornerPoint = idxOfPointsSeparator - index;
811     //     Point upperRightCorner = textDecodePOINT(index, lenOfUpperRightCornerPoint, buff);
812     //     Point lowerLeftCorner = textDecodePOINT(idxOfPointsSeparator + 1, len - lenOfUpperRightCornerPoint - 1, buff);
813     //     return new Box(upperRightCorner, lowerLeftCorner);
814     // }
815 
816     // private static Box[] textDecodeBoxArray(IntFunction!(Box[]) supplier, int index, int len, ByteBuf buff) {
817     //     // Box Array representation: {box1;box2;...boxN}
818     //     List!(Box) boxes = new ArrayList<>();
819     //     int start = index + 1;
820     //     int end = index + len - 1;
821     //     while (start < end) {
822     //         int idxOfBoxSeparator = buff.indexOf(start, end + 1, (byte) ';');
823     //         if (idxOfBoxSeparator == -1) {
824     //             // the last box
825     //             Box box = textDecodeBox(start, end - start, buff);
826     //             boxes.add(box);
827     //             break;
828     //         }
829     //         int lenOfBox = idxOfBoxSeparator - start;
830     //         Box box = textDecodeBox(start, lenOfBox, buff);
831     //         boxes.add(box);
832     //         start = idxOfBoxSeparator + 1;
833     //     }
834     //     return boxes.toArray(supplier.apply(boxes.size()));
835     // }
836 
837     // private static Path textDecodePath(int index, int len, ByteBuf buff) {
838     //     // Path representation: (p1,p2...pn) or [p1,p2...pn]
839     //     byte first = buff.getByte(index);
840     //     byte last = buff.getByte(index + len - 1);
841     //     bool isOpen;
842     //     if (first == '(' && last == ')') {
843     //         isOpen = false;
844     //     } else if (first == '[' && last == ']') {
845     //         isOpen = true;
846     //     } else {
847     //         throw new DecoderException("Decoding Path is in wrong syntax");
848     //     }
849     //     List!(Point) points = textDecodeMultiplePoints(index + 1, len - 2, buff);
850     //     return new Path(isOpen, points);
851     // }
852 
853     // private static Polygon textDecodePolygon(int index, int len, ByteBuf buff) {
854     //     // Polygon representation: (p1,p2...pn)
855     //     List!(Point) points = textDecodeMultiplePoints(index + 1, len - 2, buff);
856     //     return new Polygon(points);
857     // }
858 
859     // // this might be useful for decoding Lseg, Box, Path, Polygon Data Type.
860     // private static List!(Point) textDecodeMultiplePoints(int index, int len, ByteBuf buff) {
861     //     // representation: p1,p2,p3...pn
862     //     List!(Point) points = new ArrayList<>();
863     //     int start = index;
864     //     int end = index + len - 1;
865     //     while (start < end) {
866     //         int rightParenthesis = buff.indexOf(start, end + 1, (byte) ')');
867     //         int idxOfPointSeparator = rightParenthesis + 1;
868     //         int lenOfPoint = idxOfPointSeparator - start;
869     //         Point point = textDecodePOINT(start, lenOfPoint, buff);
870     //         points.add(point);
871     //         start = idxOfPointSeparator + 1;
872     //     }
873     //     return points;
874     // }
875 
876     // private static Circle textDecodeCircle(int index, int len, ByteBuf buff) {
877     //     // Circle representation: <p,r>
878     //     int idxOfLastComma = buff.indexOf(index + len - 1, index, (byte) ',');
879     //     int lenOfPoint = idxOfLastComma - index - 1;
880     //     Point center = textDecodePOINT(index + 1, lenOfPoint, buff);
881     //     int lenOfRadius = len - lenOfPoint - 3;
882     //     double radius = textDecodeFLOAT8(idxOfLastComma + 1, lenOfRadius, buff);
883     //     return new Circle(center, radius);
884     // }
885 
886     // private static Interval textDecodeINTERVAL(int index, int len, ByteBuf buff) {
887     //     CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
888     //     String value = cs.toString();
889     //     int years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0, microseconds = 0;
890     //     final List!(String) chunks = new ArrayList<>(7);
891     //     int idx = 0;
892     //     for (;;) {
893     //         int newIdx = value.indexOf(' ', idx);
894     //         if (newIdx == -1) {
895     //             chunks.add(value.substring(idx));
896     //             break;
897     //         }
898     //         chunks.add(value.substring(idx, newIdx));
899     //         idx = newIdx + 1;
900     //     }
901     //     bool hasTime = chunks.size() % 2 == 1;
902     //     int dateChunkMax = hasTime ? chunks.size() - 1 : chunks.size();
903     //     for (int i = 0; i < dateChunkMax; i += 2) {
904     //         int val = Integer.parseInt(chunks.get(i));
905     //         switch (chunks.get(i + 1)) {
906     //             case "year":
907     //             case "years":
908     //                 years = val;
909     //                 break;
910     //             case "mon":
911     //             case "mons":
912     //                 months = val;
913     //                 break;
914     //             case "day":
915     //             case "days":
916     //                 days = val;
917     //                 break;
918     //         }
919     //     }
920     //     if (hasTime) {
921     //         String timeChunk = chunks.get(chunks.size() - 1);
922     //         bool isNeg = timeChunk.charAt(0) == '-';
923     //         if (isNeg) timeChunk = timeChunk.substring(1);
924     //         int sidx = 0;
925     //         for (;;) {
926     //             int newIdx = timeChunk.indexOf(':', sidx);
927     //             if (newIdx == -1) {
928     //                 int m = timeChunk.substring(sidx).indexOf('.');
929     //                 if(m == -1) {
930     //                     // seconds without microseconds
931     //                     seconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx))
932     //                         : Integer.parseInt(timeChunk.substring(sidx));
933     //                 } else {
934     //                     // seconds with microseconds
935     //                     seconds =  isNeg ? -Integer.parseInt(timeChunk.substring(sidx).substring(0, m))
936     //                         : Integer.parseInt(timeChunk.substring(sidx).substring(0, m));
937     //                     microseconds = isNeg ? -Integer.parseInt(timeChunk.substring(sidx).substring(m + 1))
938     //                         : Integer.parseInt(timeChunk.substring(sidx).substring(m + 1));
939     //                 }
940     //                 break;
941     //             }
942     //             // hours
943     //             if(sidx == 0) {
944     //                 hours = isNeg ? -Integer.parseInt(timeChunk.substring(sidx, newIdx))
945     //                     : Integer.parseInt(timeChunk.substring(sidx, newIdx));
946     //             } else {
947     //                 // minutes
948     //                 minutes = isNeg ? -Integer.parseInt(timeChunk.substring(sidx, newIdx))
949     //                     : Integer.parseInt(timeChunk.substring(sidx, newIdx));
950     //             }
951     //             sidx = newIdx + 1;
952     //         }
953     //     }
954     //     return new Interval(years, months, days, hours, minutes, seconds, microseconds);
955     // }
956 
957     private static void textEncodeNUMERIC(ref Variant value, ByteBuf buff) {
958         // assert(value.type == typeid(int));
959         string s = value.toString();
960         buff.writeCharSequence(s, StandardCharsets.UTF_8);
961     }
962 
963     private static void textEncodeNUMERIC_ARRAY(ref Variant value, ByteBuf buff) {
964         if(value.type == typeid(int[])) {
965             textEncodeArray!(int)(value, DataType.NUMERIC, buff);
966         } if(value.type == typeid(long[])) {
967             textEncodeArray!(long)(value, DataType.NUMERIC, buff);
968         } if(value.type == typeid(float[])) {
969             textEncodeArray!(float)(value, DataType.NUMERIC, buff);
970         } if(value.type == typeid(double[])) {
971             textEncodeArray!(double)(value, DataType.NUMERIC, buff);
972         } else {
973             throw new Exception("Can't handle numeric array: " ~ value.type.toString());
974         }
975     }
976 
977     private static void binaryEncodeCHAR(ref Variant value, ByteBuf buff) {
978         binaryEncodeTEXT(value, buff);
979     }
980 
981     private static void binaryEncodeVARCHAR(ref Variant value, ByteBuf buff) {
982         assert(value.type == typeid(string));
983         buff.writeCharSequence(value.get!(string), StandardCharsets.UTF_8);
984     }
985     alias binaryEncodeNAME = binaryEncodeVARCHAR;
986     alias binaryEncodeBPCHAR = binaryEncodeVARCHAR;
987 
988     private static string textDecodeVARCHAR(int index, int len, ByteBuf buff) {
989         return buff.getCharSequence(index, len, StandardCharsets.UTF_8);
990     }
991     alias textDecodeCHAR = textDecodeVARCHAR;
992     alias textDecodeBPCHAR = textDecodeVARCHAR;
993     alias textdecodeTEXT = textDecodeVARCHAR;
994     alias textDecodeNAME = textDecodeVARCHAR;
995 
996     alias binaryDecodeCHAR = textDecodeVARCHAR;
997     alias binaryDecodeNAME = textDecodeVARCHAR;
998     alias binaryDecodeBPCHAR = textDecodeVARCHAR;
999     alias binaryDecodeTEXT = textDecodeVARCHAR;
1000 
1001     private static string binaryDecodeVARCHAR(int index, int len, ByteBuf buff) {
1002         return buff.getCharSequence(index, len, StandardCharsets.UTF_8);
1003     }
1004 
1005 
1006     private static void binaryEncodeTEXT(ref Variant value, ByteBuf buff) {
1007         assert(value.type == typeid(string));
1008         string s = value.toString();
1009         buff.writeCharSequence(s, StandardCharsets.UTF_8);
1010     }
1011 
1012 
1013     // private static void binaryEncodeDATE(LocalDate value, ByteBuf buff) {
1014     //     buff.writeInt((int) -value.until(LOCAL_DATE_EPOCH, ChronoUnit.DAYS));
1015     // }
1016 
1017     // private static LocalDate binaryDecodeDATE(int index, int len, ByteBuf buff) {
1018     //     return LOCAL_DATE_EPOCH.plus(buff.getInt(index), ChronoUnit.DAYS);
1019     // }
1020 
1021     // private static LocalDate textDecodeDATE(int index, int len, ByteBuf buff) {
1022     //     CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
1023     //     return LocalDate.parse(cs);
1024     // }
1025 
1026     // private static void binaryEncodeTIME(LocalTime value, ByteBuf buff) {
1027     //     buff.writeLong(value.getLong(ChronoField.MICRO_OF_DAY));
1028     // }
1029 
1030     // private static LocalTime binaryDecodeTIME(int index, int len, ByteBuf buff) {
1031     //     // micros to nanos
1032     //     return LocalTime.ofNanoOfDay(buff.getLong(index) * 1000);
1033     // }
1034 
1035     // private static LocalTime textDecodeTIME(int index, int len, ByteBuf buff) {
1036     //     CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
1037     //     return LocalTime.parse(cs);
1038     // }
1039 
1040     // private static void binaryEncodeTIMETZ(OffsetTime value, ByteBuf buff) {
1041     //     buff.writeLong(value.toLocalTime().getLong(ChronoField.MICRO_OF_DAY));
1042     //     // zone offset in seconds (should we change it to UTC ?)
1043     //     buff.writeInt(-value.getOffset().getTotalSeconds());
1044     // }
1045 
1046     // private static OffsetTime binaryDecodeTIMETZ(int index, int len, ByteBuf buff) {
1047     //     // micros to nanos
1048     //     return OffsetTime.of(LocalTime.ofNanoOfDay(buff.getLong(index) * 1000),
1049     //         // zone offset in seconds (should we change it to UTC ?)
1050     //         ZoneOffset.ofTotalSeconds(-buff.getInt(index + 8)));
1051     // }
1052 
1053     // private static OffsetTime textDecodeTIMETZ(int index, int len, ByteBuf buff) {
1054     //     CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
1055     //     return OffsetTime.parse(cs, TIMETZ_FORMAT);
1056     // }
1057 
1058     // private static void binaryEncodeTIMESTAMP(LocalDateTime value, ByteBuf buff) {
1059     //     buff.writeLong(-value.until(LOCAL_DATE_TIME_EPOCH, ChronoUnit.MICROS));
1060     // }
1061 
1062     // private static LocalDateTime binaryDecodeTIMESTAMP(int index, int len, ByteBuf buff) {
1063     //     return LOCAL_DATE_TIME_EPOCH.plus(buff.getLong(index), ChronoUnit.MICROS);
1064     // }
1065 
1066     // private static LocalDateTime textDecodeTIMESTAMP(int index, int len, ByteBuf buff) {
1067     //     CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
1068     //     return LocalDateTime.parse(cs, TIMESTAMP_FORMAT);
1069     // }
1070 
1071     // private static OffsetDateTime binaryDecodeTIMESTAMPTZ(int index, int len, ByteBuf buff) {
1072     //     return OFFSET_DATE_TIME_EPOCH.plus(buff.getLong(index), ChronoUnit.MICROS);
1073     // }
1074 
1075     // private static void binaryEncodeTIMESTAMPTZ(OffsetDateTime value, ByteBuf buff) {
1076     //     buff.writeLong(-value.until(OFFSET_DATE_TIME_EPOCH, ChronoUnit.MICROS));
1077     // }
1078 
1079     // private static OffsetDateTime textDecodeTIMESTAMPTZ(int index, int len, ByteBuf buff) {
1080     //     CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
1081     //     return OffsetDateTime.parse(cs, TIMESTAMPTZ_FORMAT);
1082     // }
1083 
1084     private static byte[] textDecodeBYTEA(int index, int len, ByteBuf buff) {
1085         if (isHexFormat(index, len, buff)) {
1086             // hex format
1087             // Shift 2 bytes: skip \x prolog
1088             return decodeHexStringToBytes(index + 2, len - 2, buff);
1089         } else {
1090             // escape format
1091             return decodeEscapeByteaStringToBuffer(index, len, buff);
1092         }
1093     }
1094 
1095     private static void binaryEncodeBYTEA(ref Variant value, ByteBuf buff) {
1096         assert(value.type == typeid(byte[]) || value.type == typeid(ubyte[]));
1097         buff.writeBytes(value.get!(byte[])());
1098     }
1099 
1100     private static byte[] binaryDecodeBYTEA(int index, int len, ByteBuf buff) {
1101         ByteBuf buffer = buff.copy(index, len);
1102         return buffer.getReadableBytes();
1103     }
1104 
1105     // private static void binaryEncodeUUID(UUID uuid, ByteBuf buff) {
1106     //     buff.writeLong(uuid.getMostSignificantBits());
1107     //     buff.writeLong(uuid.getLeastSignificantBits());
1108     // }
1109 
1110     // private static void binaryEncodePoint(Point point, ByteBuf buff) {
1111     //     binaryEncodeFLOAT8(point.x, buff);
1112     //     binaryEncodeFLOAT8(point.y, buff);
1113     // }
1114 
1115     // private static Point binaryDecodePoint(int index, int len, ByteBuf buff) {
1116     //     double x = binaryDecodeFLOAT8(index, 8, buff);
1117     //     double y = binaryDecodeFLOAT8(index + 8, 8, buff);
1118     //     return new Point(x, y);
1119     // }
1120 
1121     // private static void binaryEncodeLine(Line line, ByteBuf buff) {
1122     //     binaryEncodeFLOAT8(line.getA(), buff);
1123     //     binaryEncodeFLOAT8(line.getB(), buff);
1124     //     binaryEncodeFLOAT8(line.getC(), buff);
1125     // }
1126 
1127     // private static Line binaryDecodeLine(int index, int len, ByteBuf buff) {
1128     //     double a = binaryDecodeFLOAT8(index, 8, buff);
1129     //     double b = binaryDecodeFLOAT8(index + 8, 8, buff);
1130     //     double c = binaryDecodeFLOAT8(index + 16, 8, buff);
1131     //     return new Line(a, b, c);
1132     // }
1133 
1134     // private static void binaryEncodeLseg(LineSegment lseg, ByteBuf buff) {
1135     //     binaryEncodePoint(lseg.getP1(), buff);
1136     //     binaryEncodePoint(lseg.getP2(), buff);
1137     // }
1138 
1139     // private static LineSegment binaryDecodeLseg(int index, int len, ByteBuf buff) {
1140     //     Point p1 = binaryDecodePoint(index, 16, buff);
1141     //     Point p2 = binaryDecodePoint(index + 16, 16, buff);
1142     //     return new LineSegment(p1, p2);
1143     // }
1144 
1145     // private static void binaryEncodeBox(Box box, ByteBuf buff) {
1146     //     binaryEncodePoint(box.getUpperRightCorner(), buff);
1147     //     binaryEncodePoint(box.getLowerLeftCorner(), buff);
1148     // }
1149 
1150     // private static Box binaryDecodeBox(int index, int len, ByteBuf buff) {
1151     //     Point upperRightCorner = binaryDecodePoint(index, 16, buff);
1152     //     Point lowerLeftCorner = binaryDecodePoint(index + 16, 16, buff);
1153     //     return new Box(upperRightCorner, lowerLeftCorner);
1154     // }
1155 
1156     // private static void binaryEncodePath(Path path, ByteBuf buff) {
1157     //     if (path.isOpen()) {
1158     //         buff.writeByte(0);
1159     //     } else {
1160     //         buff.writeByte(1);
1161     //     }
1162     //     List!(Point) points = path.getPoints();
1163     //     binaryEncodeINT4(points.size(), buff);
1164     //     for (Point point : points) {
1165     //         binaryEncodePoint(point, buff);
1166     //     }
1167     // }
1168 
1169     // private static Path binaryDecodePath(int index, int len, ByteBuf buff) {
1170     //     byte first = buff.getByte(index);
1171     //     bool isOpen;
1172     //     if (first == 0) {
1173     //         isOpen = true;
1174     //     } else if (first == 1) {
1175     //         isOpen = false;
1176     //     } else {
1177     //         throw new DecoderException("Decoding Path exception");
1178     //     }
1179     //     int idx = ++index;
1180     //     int numberOfPoints = binaryDecodeINT4(idx, 4, buff);
1181     //     idx += 4;
1182     //     List!(Point) points = new ArrayList<>();
1183     //     // maybe we need some check?
1184     //     for (int i = 0; i < numberOfPoints; i++) {
1185     //         points.add(binaryDecodePoint(idx, 16, buff));
1186     //         idx += 16;
1187     //     }
1188     //     return new Path(isOpen, points);
1189     // }
1190 
1191     // private static void binaryEncodePolygon(Polygon polygon, ByteBuf buff) {
1192     //     List!(Point) points = polygon.getPoints();
1193     //     int numberOfPoints = points.size();
1194     //     binaryEncodeINT4(numberOfPoints, buff);
1195     //     for (Point point : points) {
1196     //         binaryEncodeFLOAT8(point.x, buff);
1197     //         binaryEncodeFLOAT8(point.y, buff);
1198     //     }
1199     // }
1200 
1201     // private static Polygon binaryDecodePolygon(int index, int len, ByteBuf buff) {
1202     //     int idx = index;
1203     //     int numberOfPoints = binaryDecodeINT4(index, 4, buff);
1204     //     idx += 4;
1205     //     List!(Point) points = new ArrayList<>();
1206     //     for (int i = 0; i < numberOfPoints; i++) {
1207     //         points.add(binaryDecodePoint(idx, 16, buff));
1208     //         idx += 16;
1209     //     }
1210     //     return new Polygon(points);
1211     // }
1212 
1213     // private static void binaryEncodeCircle(Circle circle, ByteBuf buff) {
1214     //     binaryEncodePoint(circle.getCenterPoint(), buff);
1215     //     binaryEncodeFLOAT8(circle.getRadius(), buff);
1216     // }
1217 
1218     // private static Circle binaryDecodeCircle(int index, int len, ByteBuf buff) {
1219     //     Point center = binaryDecodePoint(index, 16, buff);
1220     //     double radius = binaryDecodeFLOAT8(index + 16, 8, buff);
1221     //     return new Circle(center, radius);
1222     // }
1223 
1224     // private static void binaryEncodeINTERVAL(Interval interval, ByteBuf buff) {
1225     //     Duration duration = Duration
1226     //         .ofHours(interval.getHours())
1227     //         .plusMinutes(interval.getMinutes())
1228     //         .plusSeconds(interval.getSeconds())
1229     //         .plus(interval.getMicroseconds(), ChronoUnit.MICROS);
1230     //     // days won't be changed
1231     //     Period monthYear = Period.of(interval.getYears(), interval.getMonths(), interval.getDays()).normalized();
1232     //     binaryEncodeINT8(NANOSECONDS.toMicros(duration.toNanos()), buff);
1233     //     binaryEncodeINT4(monthYear.getDays(), buff);
1234     //     binaryEncodeINT4((int) monthYear.toTotalMonths(), buff);
1235     // }
1236 
1237     // private static Interval binaryDecodeINTERVAL(int index, int len, ByteBuf buff) {
1238     //     Duration duration = Duration.of(buff.getLong(index), ChronoUnit.MICROS);
1239     //     final long hours = duration.toHours();
1240     //     duration = duration.minusHours(hours);
1241     //     final long minutes = duration.toMinutes();
1242     //     duration = duration.minusMinutes(minutes);
1243     //     final long seconds = NANOSECONDS.toSeconds(duration.toNanos());
1244     //     duration = duration.minusSeconds(seconds);
1245     //     final long microseconds = NANOSECONDS.toMicros(duration.toNanos());
1246     //     int days = buff.getInt(index + 8);
1247     //     int months = buff.getInt(index + 12);
1248     //     Period monthYear = Period.of(0, months, days).normalized();
1249     //     return new Interval(monthYear.getYears(), monthYear.getMonths(), monthYear.getDays(),
1250     //         (int) hours, (int) minutes, (int) seconds, (int) microseconds);
1251     // }
1252 
1253     // private static UUID binaryDecodeUUID(int index, int len, ByteBuf buff) {
1254     //     return new UUID(buff.getLong(index), buff.getLong(index + 8));
1255     // }
1256 
1257     // private static UUID textDecodeUUID(int index, int len, ByteBuf buff) {
1258     //     return java.util.UUID.fromString(buff.getCharSequence(index, len, StandardCharsets.UTF_8).toString());
1259     // }
1260 
1261     // private static Object textDecodeJSON(int index, int len, ByteBuf buff) {
1262     //     return textDecodeJSONB(index, len, buff);
1263     // }
1264 
1265     // private static Object binaryDecodeJSON(int index, int len, ByteBuf buff) {
1266     //     return textDecodeJSONB(index, len, buff);
1267     // }
1268 
1269     // private static void binaryEncodeJSON(Object value, ByteBuf buff) {
1270     //     String s;
1271     //     if (value == Tuple.JSON_NULL) {
1272     //         s = "null";
1273     //     } else {
1274     //         s = io.vertx.core.json.Json.encode(value);
1275     //     }
1276     //     buff.writeCharSequence(s, StandardCharsets.UTF_8);
1277     // }
1278 
1279     // private static Object textDecodeJSONB(int index, int len, ByteBuf buff) {
1280 
1281     //     // Try to do without the intermediary String (?)
1282     //     CharSequence cs = buff.getCharSequence(index, len, StandardCharsets.UTF_8);
1283     //     Object value = null;
1284     //     String s = cs.toString();
1285     //     int pos = 0;
1286     //     while (pos < s.length() && Character.isWhitespace(s.charAt(pos))) {
1287     //         pos++;
1288     //     }
1289     //     if (pos == s.length()) {
1290     //         return null;
1291     //     } else if (s.charAt(pos) == '{') {
1292     //         value = new JsonObject(s);
1293     //     } else if (s.charAt(pos) == '[') {
1294     //         value = new JsonArray(s);
1295     //     } else {
1296     //         try {
1297     //             JsonNode json = Json.mapper.readTree(s);
1298     //             if (json.isNumber()) {
1299     //                 return json.numberValue();
1300     //             } else if (json.isBoolean()) {
1301     //                 return json.booleanValue();
1302     //             } else if (json.isTextual()) {
1303     //                 return json.textValue();
1304     //             } else if (json.isNull()) {
1305     //                 return Tuple.JSON_NULL;
1306     //             } else {
1307     //                 return null;
1308     //             }
1309     //         } catch (IOException e) {
1310     //             // do nothing
1311     //         }
1312     //     }
1313     //     return value;
1314     // }
1315 
1316     // private static Object binaryDecodeJSONB(int index, int len, ByteBuf buff) {
1317     //     // Skip 1 byte for version (which is 1)
1318     //     return textDecodeJSONB(index + 1, len - 1, buff);
1319     // }
1320 
1321     // private static void binaryEncodeJSONB(Object value, ByteBuf buff) {
1322     //     buff.writeByte(1); // version
1323     //     binaryEncodeJSON(value, buff);
1324     // }
1325 
1326     /**
1327      * Decode the specified {@code buff} formatted as a decimal string starting at the readable index
1328      * with the specified {@code length} to a long.
1329      *
1330      * @param index the hex string index
1331      * @param len the hex string length
1332      * @param buff the byte buff to read from
1333      * @return the decoded value as a long
1334      */
1335     private static long decodeDecStringToLong(int index, int len, ByteBuf buff) {
1336         long value = 0;
1337         string v = textdecodeTEXT(index, len, buff);
1338         return v.to!long();
1339         // if (len > 0) {
1340         //     int to = index + len;
1341         //     bool neg = false;
1342         //     if (buff.getByte(index) == '-') {
1343         //         neg = true;
1344         //         index++;
1345         //     }
1346         //     while (index < to) {
1347         //         byte ch = buff.getByte(index++);
1348         //         byte nibble = cast(byte)(ch - '0');
1349         //         value = value * 10 + nibble;
1350         //     }
1351         //     if (neg) {
1352         //         value = -value;
1353         //     }
1354         // }
1355         // return value;
1356     }
1357 
1358     /**
1359      * Decode the specified {@code buff} formatted as an hex string starting at the buffer readable index
1360      * with the specified {@code length} to a {@link Buffer}.
1361      *
1362      * @param len the hex string length
1363      * @param buff the byte buff to read from
1364      * @return the decoded value as a Buffer
1365      */
1366     private static byte[] decodeHexStringToBytes(int index, int len, ByteBuf buff) {
1367         len = len >> 1;
1368         byte[] buffer = new byte[len];
1369         for (int i = 0; i < len; i++) {
1370             byte b0 = decodeHexChar(buff.getByte(index++));
1371             byte b1 = decodeHexChar(buff.getByte(index++));
1372             // buffer.appendByte((byte) (b0 * 16 + b1));
1373             buffer[i] = cast(byte) (b0 * 16 + b1);
1374         }
1375         return buffer;
1376     }
1377 
1378     private static byte decodeHexChar(byte ch) {
1379         return cast(byte)(((ch & 0x1F) + ((ch >> 6) * 0x19) - 0x10) & 0x0F);
1380     }
1381 
1382     private static bool isHexFormat(int index, int len, ByteBuf buff) {
1383         return len >= 2 && buff.getByte(index) == '\\' && buff.getByte(index + 1) == 'x';
1384     }
1385 
1386     private static byte[] decodeEscapeByteaStringToBuffer(int index, int len, ByteBuf buff) {
1387         // Buffer buffer = Buffer.buffer();
1388         Appender!(byte[]) buffer;
1389         buffer.reserve(len);
1390 
1391         int pos = 0;
1392         while (pos < len) {
1393             byte current = buff.getByte(pos + index);
1394 
1395             if (current == '\\') {
1396                 if (pos + 2 <= len && buff.getByte(pos + index + 1) == '\\') {
1397                     // check double backslashes
1398                     buffer.put(cast(byte) '\\');
1399                     pos += 2;
1400                 } else if (pos + 4 <= len) {
1401                     // a preceded backslash with three-digit octal value
1402                     // int high = Character.digit(buff.getByte(pos + index + 1), 8) << 6;
1403                     // int medium = Character.digit(buff.getByte(pos + index + 2), 8) << 3;
1404                     // int low = Character.digit(buff.getByte(pos + index + 3), 8);
1405                     // int escapedValue = high + medium + low;
1406                     byte[] data = new byte[3];
1407                     buff.getBytes(pos + index + 1, data);
1408                     short escapedValue = to!short(cast(string)data, 8);
1409 
1410                     buffer.put(cast(byte) escapedValue);
1411                     pos += 4;
1412                 } else {
1413                     throw new DecoderException("Decoding unexpected BYTEA escape format");
1414                 }
1415             } else {
1416                 // printable octets
1417                 buffer.put(current);
1418                 pos++;
1419             }
1420         }
1421 
1422         return buffer.data();
1423     }
1424 
1425     // private static <T> T[] binaryDecodeArray(IntFunction!(T[]) supplier, DataType type, int index, int len, ByteBuf buff) {
1426     //     if (len == 12) {
1427     //         return supplier.apply(0);
1428     //     }
1429     //     int dim = buff.getInt(index);    // read ndim
1430     //     index += 4;
1431     //     index += 4;                      // skip dataoffset
1432     //     index += 4;                      // skip elemtype
1433     //     int length = buff.getInt(index); // read dimensions
1434     //     index += 4;
1435     //     index += 4;                      // skip lower bnds
1436     //     if (dim != 1) {
1437     //         logger.warn("Only arrays of dimension 1 are supported");
1438     //         return null;
1439     //     }
1440     //     T[] array = supplier.apply(length);
1441     //     for (int i = 0; i < array.length; i++) {
1442     //         int l = buff.getInt(index);
1443     //         index += 4;
1444     //         if (l != -1) {
1445     //             array[i] = (T) decodeBinary(type, index, l, buff);
1446     //             index += l;
1447     //         }
1448     //     }
1449     //     return array;
1450     // }
1451 
1452     private static void binaryEncodeArray(T)(ref Variant data, DataType type, ByteBuf buff){
1453         assert(data.type == typeid(T[]));
1454         T[] values = data.get!(T[])();
1455 
1456         int startIndex = buff.writerIndex();
1457         buff.writeInt(1);             // ndim
1458         buff.writeInt(0);             // dataoffset
1459         buff.writeInt(cast(int)type);       // elemtype
1460         buff.writeInt(cast(int)values.length); // dimension
1461         buff.writeInt(1);             // lower bnds
1462         bool hasNulls = false;
1463         foreach (T value ; values) {
1464             static if(is(T == class) || is(T == interface)) {
1465                 if (value is null) {
1466                     hasNulls = true;
1467                     buff.writeInt(-1);
1468                     continue;
1469                 }
1470             } 
1471 
1472             int idx = buff.writerIndex();
1473             buff.writeInt(0);
1474             static if(is(T == Variant)) {
1475                 encodeBinary(type, value, buff);
1476             } else {
1477                 Variant v = value;
1478                 encodeBinary(type, v, buff);
1479             }
1480             buff.setInt(idx, buff.writerIndex() - idx - 4);
1481         }
1482         if (hasNulls) {
1483             buff.setInt(startIndex + 4, 1);
1484         }
1485     }
1486 
1487     // private static T[] textDecodeArray(T)(IntFunction!(T[]) supplier, DataType type, int index, int len, ByteBuf buff) {
1488     //     List!(T) list = new ArrayList<>();
1489     //     int from = index + 1; // Set index after '{'
1490     //     int to = index + len - 1; // Set index before '}'
1491     //     while (from < to) {
1492     //         // Escaped content ?
1493     //         bool escaped = buff.getByte(from) == '"';
1494     //         int idx;
1495     //         if (escaped) {
1496     //             idx = buff.forEachByte(from, to - from, new UTF8StringEndDetector());
1497     //             idx = buff.indexOf(idx, to, (byte) ','); // SEE iF WE CAN GET RID oF IT
1498     //         } else {
1499     //             idx = buff.indexOf(from, to, (byte) ',');
1500     //         }
1501     //         if (idx == -1) {
1502     //             idx = to;
1503     //         }
1504     //         T elt = textDecodeArrayElement(type, from, idx - from, buff);
1505     //         list.add(elt);
1506     //         from = idx + 1;
1507     //     }
1508     //     return list.toArray(supplier.apply(list.size()));
1509     // }
1510 
1511     // private static T textDecodeArrayElement(T)(DataType type, int index, int len, ByteBuf buff) {
1512     //     if (len == 4
1513     //         && toUpper(buff.getByte(index)) == 'N'
1514     //         && toUpper(buff.getByte(index + 1)) == 'U'
1515     //         && toUpper(buff.getByte(index + 2)) == 'L'
1516     //         && toUpper(buff.getByte(index + 3)) == 'L' ) {
1517     //         return null;
1518     //     } else {
1519     //         bool escaped = buff.getByte(index) == '"';
1520     //         if (escaped) {
1521     //             // Some escaping - improve that later...
1522     //             string s = buff.toString(index + 1, len - 2, StandardCharsets.UTF_8);
1523     //             Appender!string sb; // = new StringBuilder();
1524     //             sb.reserve(s.length);
1525     //             for (int i = 0;i < cast(int)s.length;i++) {
1526     //                 char c = s[i];
1527     //                 if (c == '\\') {
1528     //                     c = s[++i];
1529     //                 }
1530     //                 sb.put(c);
1531     //             }
1532     //             buff = Unpooled.copiedBuffer(sb.data, StandardCharsets.UTF_8);
1533     //             index = 0;
1534     //             len = buff.readableBytes();
1535     //         }
1536     //         return cast(T) decodeText(type, index, len, buff);
1537     //     }
1538     // }
1539 
1540     private static void textEncodeArray(T)(ref Variant data, DataType type, ByteBuf buff){
1541         assert(data.type == typeid(T[]));
1542         T[] values = data.get!(T[])();
1543 
1544         buff.writeByte('{');
1545         int len = cast(int)values.length;
1546         for (int i = 0; i < len; i++) {
1547             if (i > 0) {
1548                 buff.writeByte(',');
1549             }
1550             T value = values[i];
1551             static if(is(T == class) || is(T == interface)) {
1552                 if (value is null) {
1553                     buff.writeByte('N');
1554                     buff.writeByte('U');
1555                     buff.writeByte('L');
1556                     buff.writeByte('L');
1557                     continue;
1558                 } 
1559             }
1560             
1561             Variant v = value;
1562             textEncode(type, v, buff);
1563         }
1564         buff.writeByte('}');
1565     }
1566 }