View Javadoc
1   package org.djutils.serialization;
2   
3   import java.nio.charset.Charset;
4   import java.util.ArrayList;
5   import java.util.HashMap;
6   import java.util.List;
7   import java.util.Map;
8   
9   import org.djunits.unit.Unit;
10  import org.djunits.value.vdouble.matrix.base.DoubleMatrix;
11  import org.djunits.value.vdouble.scalar.base.DoubleScalar;
12  import org.djunits.value.vdouble.vector.base.DoubleVector;
13  import org.djunits.value.vfloat.matrix.base.FloatMatrix;
14  import org.djunits.value.vfloat.scalar.base.FloatScalar;
15  import org.djunits.value.vfloat.vector.base.FloatVector;
16  import org.djutils.exceptions.Throw;
17  import org.djutils.serialization.serializers.BasicPrimitiveArrayOrMatrixSerializer;
18  import org.djutils.serialization.serializers.DoubleMatrixSerializer;
19  import org.djutils.serialization.serializers.DoubleScalarSerializer;
20  import org.djutils.serialization.serializers.DoubleVectorArraySerializer;
21  import org.djutils.serialization.serializers.DoubleVectorSerializer;
22  import org.djutils.serialization.serializers.FixedSizeObjectSerializer;
23  import org.djutils.serialization.serializers.FloatMatrixSerializer;
24  import org.djutils.serialization.serializers.FloatScalarSerializer;
25  import org.djutils.serialization.serializers.FloatVectorArraySerializer;
26  import org.djutils.serialization.serializers.FloatVectorSerializer;
27  import org.djutils.serialization.serializers.ObjectArraySerializer;
28  import org.djutils.serialization.serializers.ObjectMatrixSerializer;
29  import org.djutils.serialization.serializers.ObjectSerializer;
30  import org.djutils.serialization.serializers.Pointer;
31  import org.djutils.serialization.serializers.Serializer;
32  import org.djutils.serialization.serializers.StringArraySerializer;
33  import org.djutils.serialization.serializers.StringMatrixSerializer;
34  
35  /**
36   * Serialization and deserialization of a single object. These take into account the endianness for coding the different values.
37   * Java is by default big-endian.
38   * <p>
39   * Copyright (c) 2016-2025 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
40   * BSD-style license. See <a href="https://sim0mq.org/docs/current/license.html">OpenTrafficSim License</a>.
41   * </p>
42   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
43   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
44   */
45  public final class TypedObject
46  {
47      /**
48       * Do not instantiate this utility class.
49       */
50      private TypedObject()
51      {
52          // Utility class; do not instantiate.
53      }
54  
55      /** The easy converters keyed by Class. */
56      protected static final Map<Class<?>, Serializer<?>> ENCODERS = new HashMap<>();
57  
58      /** All the converters that decode into primitive data when possible, keyed by prefix. */
59      protected static final Map<Byte, Serializer<?>> PRIMITIVE_DATA_DECODERS = new HashMap<>();
60  
61      /** All the converters that decode into arrays and matrices of Objects, keyed by prefix. */
62      protected static final Map<Byte, Serializer<?>> OBJECT_DECODERS = new HashMap<>();
63  
64      /** Converter for Byte. */
65      protected static final Serializer<Byte> CONVERT_BYTE = new FixedSizeObjectSerializer<Byte>(FieldTypes.BYTE_8, 1, "Byte_8")
66      {
67          @Override
68          public void serialize(final Byte object, final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
69          {
70              buffer[pointer.getAndIncrement(1)] = object;
71          }
72  
73          @Override
74          public Byte deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
75          {
76              return buffer[pointer.getAndIncrement(1)];
77          }
78      };
79  
80      /** Converter for Short. */
81      protected static final Serializer<Short> CONVERT_SHORT =
82              new FixedSizeObjectSerializer<Short>(FieldTypes.SHORT_16, 2, "Short_16")
83              {
84                  @Override
85                  public void serialize(final Short object, final byte[] buffer, final Pointer pointer,
86                          final EndianUtil endianUtil)
87                  {
88                      endianUtil.encodeShort(object, buffer, pointer.getAndIncrement(2));
89                  }
90  
91                  @Override
92                  public Short deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
93                  {
94                      return endianUtil.decodeShort(buffer, pointer.getAndIncrement(2));
95                  }
96              };
97  
98      /** Converter for Integer. */
99      protected static final Serializer<Integer> CONVERT_INTEGER =
100             new FixedSizeObjectSerializer<Integer>(FieldTypes.INT_32, 4, "Integer_32")
101             {
102                 @Override
103                 public void serialize(final Integer object, final byte[] buffer, final Pointer pointer,
104                         final EndianUtil endianUtil)
105                 {
106                     endianUtil.encodeInt(object, buffer, pointer.getAndIncrement(4));
107                 }
108 
109                 @Override
110                 public Integer deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
111                 {
112                     return endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
113                 }
114             };
115 
116     /** Converter for Integer. */
117     protected static final Serializer<Long> CONVERT_LONG = new FixedSizeObjectSerializer<Long>(FieldTypes.LONG_64, 8, "Long_64")
118     {
119         @Override
120         public void serialize(final Long object, final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
121         {
122             endianUtil.encodeLong(object, buffer, pointer.getAndIncrement(8));
123         }
124 
125         @Override
126         public Long deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
127         {
128             return endianUtil.decodeLong(buffer, pointer.getAndIncrement(8));
129         }
130     };
131 
132     /** Converter for Float. */
133     protected static final Serializer<Float> CONVERT_FLOAT =
134             new FixedSizeObjectSerializer<Float>(FieldTypes.FLOAT_32, 4, "Float_32")
135             {
136                 @Override
137                 public void serialize(final Float object, final byte[] buffer, final Pointer pointer,
138                         final EndianUtil endianUtil)
139                 {
140                     endianUtil.encodeFloat(object, buffer, pointer.getAndIncrement(4));
141                 }
142 
143                 @Override
144                 public Float deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
145                 {
146                     return endianUtil.decodeFloat(buffer, pointer.getAndIncrement(4));
147                 }
148             };
149 
150     /** Converter for Double. */
151     protected static final Serializer<Double> CONVERT_DOUBLE =
152             new FixedSizeObjectSerializer<Double>(FieldTypes.DOUBLE_64, 8, "Double_64")
153             {
154                 @Override
155                 public void serialize(final Double object, final byte[] buffer, final Pointer pointer,
156                         final EndianUtil endianUtil)
157                 {
158                     endianUtil.encodeDouble(object, buffer, pointer.getAndIncrement(8));
159                 }
160 
161                 @Override
162                 public Double deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
163                 {
164                     return endianUtil.decodeDouble(buffer, pointer.getAndIncrement(8));
165                 }
166             };
167 
168     /** Converter for Boolean. */
169     protected static final Serializer<Boolean> CONVERT_BOOLEAN =
170             new FixedSizeObjectSerializer<Boolean>(FieldTypes.BOOLEAN_8, 1, "Boolean_8")
171             {
172                 @Override
173                 public void serialize(final Boolean object, final byte[] buffer, final Pointer pointer,
174                         final EndianUtil endianUtil)
175                 {
176                     buffer[pointer.getAndIncrement(1)] = (byte) (object ? 1 : 0);
177                 }
178 
179                 @Override
180                 public Boolean deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
181                 {
182                     return buffer[pointer.getAndIncrement(1)] != 0;
183                 }
184             };
185 
186     /** Converter for Character. */
187     protected static final Serializer<Character> CONVERT_CHARACTER16 =
188             new FixedSizeObjectSerializer<Character>(FieldTypes.CHAR_16, 2, "Char_16")
189             {
190                 @Override
191                 public void serialize(final Character object, final byte[] buffer, final Pointer pointer,
192                         final EndianUtil endianUtil)
193                 {
194                     endianUtil.encodeChar(object, buffer, pointer.getAndIncrement(size(object)));
195                 }
196 
197                 @Override
198                 public Character deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
199                 {
200                     return endianUtil.decodeChar(buffer, pointer.getAndIncrement(2));
201                 }
202             };
203 
204     /** Converter for Character. */
205     protected static final Serializer<Character> CONVERT_CHARACTER8 =
206             new FixedSizeObjectSerializer<Character>(FieldTypes.CHAR_8, 1, "Char_8")
207             {
208                 @Override
209                 public void serialize(final Character object, final byte[] buffer, final Pointer pointer,
210                         final EndianUtil endianUtil)
211                 {
212                     buffer[pointer.getAndIncrement(size(object))] = (byte) (object & 0xFF);
213                 }
214 
215                 @Override
216                 public Character deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
217                 {
218                     return Character.valueOf((char) buffer[pointer.getAndIncrement(1)]);
219                 }
220             };
221 
222     /** Converter for String. */
223     protected static final Serializer<String> CONVERT_STRING16 =
224             new ObjectSerializer<String>(FieldTypes.STRING_UTF16, "String_16")
225             {
226                 @Override
227                 public int size(final String object)
228                 {
229                     return 4 + object.getBytes(UTF16).length;
230                 }
231 
232                 @Override
233                 public void serialize(final String string, final byte[] buffer, final Pointer pointer,
234                         final EndianUtil endianUtil)
235                 {
236                     // Note that according to https://stackoverflow.com/questions/74887443, String.length returns the number of
237                     // code units (i.e. the number of 16-bit char values) needed to make up the String and not the number of
238                     // Unicode codepoints.
239                     char[] chars = new char[string.length()];
240                     string.getChars(0, chars.length, chars, 0);
241                     endianUtil.encodeInt(chars.length, buffer, pointer.getAndIncrement(4));
242                     for (char c : chars)
243                     {
244                         endianUtil.encodeChar(c, buffer, pointer.getAndIncrement(2));
245                     }
246                 }
247 
248                 @Override
249                 public String deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
250                 {
251                     String s = endianUtil.decodeUTF16String(buffer, pointer.get());
252                     pointer.getAndIncrement(4 + s.length() * 2);
253                     return s;
254                 }
255             };
256 
257     /** Converter for String. */
258     protected static final Serializer<String> CONVERT_STRING8 = new ObjectSerializer<String>(FieldTypes.STRING_UTF8, "String_8")
259     {
260         @Override
261         public int size(final String string)
262         {
263             return 4 + string.getBytes(UTF8).length;
264         }
265 
266         @Override
267         public void serialize(final String string, final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
268         {
269             byte[] s = string.getBytes(UTF8);
270             endianUtil.encodeInt(s.length, buffer, pointer.getAndIncrement(4));
271             for (byte b : s)
272             {
273                 buffer[pointer.getAndIncrement(1)] = b;
274             }
275         }
276 
277         @Override
278         public String deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
279                 throws SerializationException
280         {
281             int bytesUsed = endianUtil.decodeInt(buffer, pointer.get());
282             String s = endianUtil.decodeUTF8String(buffer, pointer.get());
283             pointer.getAndIncrement(4 + bytesUsed);
284             return s;
285         }
286     };
287 
288     /** Converter for byte array. */
289     protected static final Serializer<byte[]> CONVERT_BT_ARRAY =
290             new BasicPrimitiveArrayOrMatrixSerializer<byte[]>(FieldTypes.BYTE_8_ARRAY, 1, "byte_8_array", 1)
291             {
292                 @Override
293                 public int size(final byte[] array)
294                 {
295                     return 4 + getElementSize() * array.length;
296                 }
297 
298                 @Override
299                 public void serialize(final byte[] array, final byte[] buffer, final Pointer pointer,
300                         final EndianUtil endianUtil) throws SerializationException
301                 {
302                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
303                     for (int i = 0; i < array.length; i++)
304                     {
305                         buffer[pointer.getAndIncrement(getElementSize())] = array[i];
306                     }
307                 }
308 
309                 @Override
310                 public byte[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
311                         throws SerializationException
312                 {
313                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
314                     byte[] result = new byte[size];
315                     for (int i = 0; i < size; i++)
316                     {
317                         result[i] = buffer[pointer.getAndIncrement(getElementSize())];
318                     }
319                     return result;
320                 }
321             };
322 
323     /** Converter for Byte array. */
324     protected static final Serializer<Byte[]> CONVERT_BYTE_ARRAY =
325             new ObjectArraySerializer<Byte>(FieldTypes.BYTE_8_ARRAY, 1, Byte.valueOf((byte) 0), "Byte_8_array")
326             {
327                 @Override
328                 public void serializeElement(final Byte object, final byte[] buffer, final int offset,
329                         final EndianUtil endianUtil)
330                 {
331                     buffer[offset] = object;
332                 }
333 
334                 @Override
335                 public Byte deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
336                 {
337                     return buffer[offset];
338                 }
339             };
340 
341     /** Converter for short array. */
342     protected static final Serializer<short[]> CONVERT_SHRT_ARRAY =
343             new BasicPrimitiveArrayOrMatrixSerializer<short[]>(FieldTypes.SHORT_16_ARRAY, 2, "short_16_array", 1)
344             {
345                 @Override
346                 public int size(final short[] array)
347                 {
348                     return 4 + getElementSize() * array.length;
349                 }
350 
351                 @Override
352                 public void serialize(final short[] array, final byte[] buffer, final Pointer pointer,
353                         final EndianUtil endianUtil) throws SerializationException
354                 {
355                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
356                     for (int i = 0; i < array.length; i++)
357                     {
358                         endianUtil.encodeShort(array[i], buffer, pointer.getAndIncrement(getElementSize()));
359                     }
360                 }
361 
362                 @Override
363                 public short[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
364                         throws SerializationException
365                 {
366                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
367                     short[] result = new short[size];
368                     for (int i = 0; i < size; i++)
369                     {
370                         result[i] = endianUtil.decodeShort(buffer, pointer.getAndIncrement(getElementSize()));
371                     }
372                     return result;
373                 }
374             };
375 
376     /** Converter for Short array. */
377     protected static final Serializer<Short[]> CONVERT_SHORT_ARRAY =
378             new ObjectArraySerializer<Short>(FieldTypes.SHORT_16_ARRAY, 2, Short.valueOf((short) 0), "Short_16_array")
379             {
380                 @Override
381                 public void serializeElement(final Short object, final byte[] buffer, final int offset,
382                         final EndianUtil endianUtil)
383                 {
384                     endianUtil.encodeShort(object, buffer, offset);
385                 }
386 
387                 @Override
388                 public Short deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
389                 {
390                     return endianUtil.decodeShort(buffer, offset);
391                 }
392             };
393 
394     /** Converter for int array. */
395     protected static final Serializer<int[]> CONVERT_INT_ARRAY =
396             new BasicPrimitiveArrayOrMatrixSerializer<int[]>(FieldTypes.INT_32_ARRAY, 4, "int_32_array", 1)
397             {
398                 @Override
399                 public int size(final int[] array)
400                 {
401                     return 4 + getElementSize() * array.length;
402                 }
403 
404                 @Override
405                 public void serialize(final int[] array, final byte[] buffer, final Pointer pointer,
406                         final EndianUtil endianUtil) throws SerializationException
407                 {
408                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
409                     for (int i = 0; i < array.length; i++)
410                     {
411                         endianUtil.encodeInt(array[i], buffer, pointer.getAndIncrement(getElementSize()));
412                     }
413                 }
414 
415                 @Override
416                 public int[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
417                         throws SerializationException
418                 {
419                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
420                     int[] result = new int[size];
421                     for (int i = 0; i < size; i++)
422                     {
423                         result[i] = endianUtil.decodeInt(buffer, pointer.getAndIncrement(getElementSize()));
424                     }
425                     return result;
426                 }
427             };
428 
429     /** Converter for Integer array. */
430     protected static final Serializer<Integer[]> CONVERT_INTEGER_ARRAY =
431             new ObjectArraySerializer<Integer>(FieldTypes.INT_32_ARRAY, 4, Integer.valueOf(0), "Integer_32_array")
432             {
433                 @Override
434                 public void serializeElement(final Integer object, final byte[] buffer, final int offset,
435                         final EndianUtil endianUtil)
436                 {
437                     endianUtil.encodeInt(object, buffer, offset);
438                 }
439 
440                 @Override
441                 public Integer deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
442                 {
443                     return endianUtil.decodeInt(buffer, offset);
444                 }
445             };
446 
447     /** Converter for long array. */
448     protected static final Serializer<long[]> CONVERT_LNG_ARRAY =
449             new BasicPrimitiveArrayOrMatrixSerializer<long[]>(FieldTypes.LONG_64_ARRAY, 8, "long_64_array", 1)
450             {
451                 @Override
452                 public int size(final long[] array)
453                 {
454                     return 4 + getElementSize() * array.length;
455                 }
456 
457                 @Override
458                 public void serialize(final long[] array, final byte[] buffer, final Pointer pointer,
459                         final EndianUtil endianUtil) throws SerializationException
460                 {
461                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
462                     for (int i = 0; i < array.length; i++)
463                     {
464                         endianUtil.encodeLong(array[i], buffer, pointer.getAndIncrement(getElementSize()));
465                     }
466                 }
467 
468                 @Override
469                 public long[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
470                         throws SerializationException
471                 {
472                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
473                     long[] result = new long[size];
474                     for (int i = 0; i < size; i++)
475                     {
476                         result[i] = endianUtil.decodeLong(buffer, pointer.getAndIncrement(getElementSize()));
477                     }
478                     return result;
479                 }
480             };
481 
482     /** Converter for Long array. */
483     protected static final Serializer<Long[]> CONVERT_LONG_ARRAY =
484             new ObjectArraySerializer<Long>(FieldTypes.LONG_64_ARRAY, 8, Long.valueOf(0), "Long_64_array")
485             {
486                 @Override
487                 public void serializeElement(final Long object, final byte[] buffer, final int offset,
488                         final EndianUtil endianUtil)
489                 {
490                     endianUtil.encodeLong(object, buffer, offset);
491                 }
492 
493                 @Override
494                 public Long deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
495                 {
496                     return endianUtil.decodeLong(buffer, offset);
497                 }
498             };
499 
500     /** Converter for float array. */
501     protected static final Serializer<float[]> CONVERT_FLT_ARRAY =
502             new BasicPrimitiveArrayOrMatrixSerializer<float[]>(FieldTypes.FLOAT_32_ARRAY, 4, "float_32_array", 1)
503             {
504                 @Override
505                 public int size(final float[] array)
506                 {
507                     return 4 + getElementSize() * array.length;
508                 }
509 
510                 @Override
511                 public void serialize(final float[] array, final byte[] buffer, final Pointer pointer,
512                         final EndianUtil endianUtil) throws SerializationException
513                 {
514                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
515                     for (int i = 0; i < array.length; i++)
516                     {
517                         endianUtil.encodeFloat(array[i], buffer, pointer.getAndIncrement(getElementSize()));
518                     }
519                 }
520 
521                 @Override
522                 public float[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
523                         throws SerializationException
524                 {
525                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
526                     float[] result = new float[size];
527                     for (int i = 0; i < size; i++)
528                     {
529                         result[i] = endianUtil.decodeFloat(buffer, pointer.getAndIncrement(getElementSize()));
530                     }
531                     return result;
532                 }
533             };
534 
535     /** Converter for Float array. */
536     protected static final Serializer<Float[]> CONVERT_FLOAT_ARRAY =
537             new ObjectArraySerializer<Float>(FieldTypes.FLOAT_32_ARRAY, 4, Float.valueOf(0), "Float_32_array")
538             {
539                 @Override
540                 public void serializeElement(final Float object, final byte[] buffer, final int offset,
541                         final EndianUtil endianUtil)
542                 {
543                     endianUtil.encodeFloat(object, buffer, offset);
544                 }
545 
546                 @Override
547                 public Float deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
548                 {
549                     return endianUtil.decodeFloat(buffer, offset);
550                 }
551             };
552 
553     /** Converter for double array. */
554     protected static final Serializer<double[]> CONVERT_DBL_ARRAY =
555             new BasicPrimitiveArrayOrMatrixSerializer<double[]>(FieldTypes.DOUBLE_64_ARRAY, 8, "double_64_array", 1)
556             {
557                 @Override
558                 public int size(final double[] array)
559                 {
560                     return 4 + getElementSize() * array.length;
561                 }
562 
563                 @Override
564                 public void serialize(final double[] array, final byte[] buffer, final Pointer pointer,
565                         final EndianUtil endianUtil) throws SerializationException
566                 {
567                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
568                     for (int i = 0; i < array.length; i++)
569                     {
570                         endianUtil.encodeDouble(array[i], buffer, pointer.getAndIncrement(getElementSize()));
571                     }
572                 }
573 
574                 @Override
575                 public double[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
576                         throws SerializationException
577                 {
578                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
579                     double[] result = new double[size];
580                     for (int i = 0; i < size; i++)
581                     {
582                         result[i] = endianUtil.decodeDouble(buffer, pointer.getAndIncrement(getElementSize()));
583                     }
584                     return result;
585                 }
586             };
587 
588     /** Converter for Double array. */
589     protected static final Serializer<Double[]> CONVERT_DOUBLE_ARRAY =
590             new ObjectArraySerializer<Double>(FieldTypes.DOUBLE_64_ARRAY, 8, Double.valueOf(0), "Double_64_array")
591             {
592                 @Override
593                 public void serializeElement(final Double object, final byte[] buffer, final int offset,
594                         final EndianUtil endianUtil)
595                 {
596                     endianUtil.encodeDouble(object, buffer, offset);
597                 }
598 
599                 @Override
600                 public Double deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
601                 {
602                     return endianUtil.decodeDouble(buffer, offset);
603                 }
604             };
605 
606     /** Converter for boolean array. */
607     protected static final Serializer<boolean[]> CONVERT_BOOL_ARRAY =
608             new BasicPrimitiveArrayOrMatrixSerializer<boolean[]>(FieldTypes.BOOLEAN_8_ARRAY, 1, "bool_8_array", 1)
609             {
610                 @Override
611                 public int size(final boolean[] array)
612                 {
613                     return 4 + getElementSize() * array.length;
614                 }
615 
616                 @Override
617                 public void serialize(final boolean[] array, final byte[] buffer, final Pointer pointer,
618                         final EndianUtil endianUtil) throws SerializationException
619                 {
620                     endianUtil.encodeInt(array.length, buffer, pointer.getAndIncrement(4));
621                     for (int i = 0; i < array.length; i++)
622                     {
623                         buffer[pointer.getAndIncrement(getElementSize())] = (byte) (array[i] ? 1 : 0);
624                     }
625                 }
626 
627                 @Override
628                 public boolean[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
629                         throws SerializationException
630                 {
631                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
632                     boolean[] result = new boolean[size];
633                     for (int i = 0; i < size; i++)
634                     {
635                         result[i] = buffer[pointer.getAndIncrement(getElementSize())] != 0;
636                     }
637                     return result;
638                 }
639             };
640 
641     /** Converter for Boolean array. */
642     protected static final Serializer<Boolean[]> CONVERT_BOOLEAN_ARRAY =
643             new ObjectArraySerializer<Boolean>(FieldTypes.BOOLEAN_8_ARRAY, 1, Boolean.FALSE, "Boolean_8_array")
644             {
645                 @Override
646                 public void serializeElement(final Boolean object, final byte[] buffer, final int offset,
647                         final EndianUtil endianUtil)
648                 {
649                     buffer[offset] = (byte) (object ? 1 : 0);
650                 }
651 
652                 @Override
653                 public Boolean deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
654                 {
655                     return buffer[offset] != 0;
656                 }
657             };
658 
659     /** Converter for byte matrix. */
660     protected static final Serializer<byte[][]> CONVERT_BT_MATRIX =
661             new BasicPrimitiveArrayOrMatrixSerializer<byte[][]>(FieldTypes.BYTE_8_MATRIX, 1, "byte_8_matrix", 2)
662             {
663                 @Override
664                 public int size(final byte[][] matrix)
665                 {
666                     return 8 + getElementSize() * matrix.length * matrix[0].length;
667                 }
668 
669                 @Override
670                 public void serialize(final byte[][] matrix, final byte[] buffer, final Pointer pointer,
671                         final EndianUtil endianUtil) throws SerializationException
672                 {
673                     int height = matrix.length;
674                     int width = matrix[0].length;
675                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
676                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
677                     for (int i = 0; i < height; i++)
678                     {
679                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
680                         for (int j = 0; j < width; j++)
681                         {
682                             buffer[pointer.getAndIncrement(getElementSize())] = matrix[i][j];
683                         }
684                     }
685                 }
686 
687                 @Override
688                 public byte[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
689                         throws SerializationException
690                 {
691                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
692                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
693                     byte[][] result = new byte[height][width];
694                     for (int i = 0; i < height; i++)
695                     {
696                         for (int j = 0; j < width; j++)
697                         {
698                             result[i][j] = buffer[pointer.getAndIncrement(getElementSize())];
699                         }
700                     }
701                     return result;
702                 }
703             };
704 
705     /** Converter for Byte matrix. */
706     protected static final Serializer<Byte[][]> CONVERT_BYTE_MATRIX =
707             new ObjectMatrixSerializer<Byte>(FieldTypes.BYTE_8_MATRIX, 1, Byte.class, "Byte_8_matrix")
708             {
709                 @Override
710                 public void serializeElement(final Byte object, final byte[] buffer, final int offset,
711                         final EndianUtil endianUtil)
712                 {
713                     buffer[offset] = object;
714                 }
715 
716                 @Override
717                 public Byte deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
718                 {
719                     return buffer[offset];
720                 }
721             };
722 
723     /** Converter for short matrix. */
724     protected static final Serializer<short[][]> CONVERT_SHRT_MATRIX =
725             new BasicPrimitiveArrayOrMatrixSerializer<short[][]>(FieldTypes.SHORT_16_MATRIX, 2, "short_16_matrix", 2)
726             {
727                 @Override
728                 public int size(final short[][] matrix)
729                 {
730                     return 8 + getElementSize() * matrix.length * matrix[0].length;
731                 }
732 
733                 @Override
734                 public void serialize(final short[][] matrix, final byte[] buffer, final Pointer pointer,
735                         final EndianUtil endianUtil) throws SerializationException
736                 {
737                     int height = matrix.length;
738                     int width = matrix[0].length;
739                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
740                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
741                     for (int i = 0; i < height; i++)
742                     {
743                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
744                         for (int j = 0; j < width; j++)
745                         {
746                             endianUtil.encodeShort(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
747                         }
748                     }
749                 }
750 
751                 @Override
752                 public short[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
753                         throws SerializationException
754                 {
755                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
756                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
757                     short[][] result = new short[height][width];
758                     for (int i = 0; i < height; i++)
759                     {
760                         for (int j = 0; j < width; j++)
761                         {
762                             result[i][j] = endianUtil.decodeShort(buffer, pointer.getAndIncrement(getElementSize()));
763                         }
764                     }
765                     return result;
766                 }
767             };
768 
769     /** Converter for Short matrix. */
770     protected static final Serializer<Short[][]> CONVERT_SHORT_MATRIX =
771             new ObjectMatrixSerializer<Short>(FieldTypes.SHORT_16_MATRIX, 2, Short.class, "Short_16_matrix")
772             {
773                 @Override
774                 public void serializeElement(final Short object, final byte[] buffer, final int offset,
775                         final EndianUtil endianUtil)
776                 {
777                     endianUtil.encodeShort(object, buffer, offset);
778                 }
779 
780                 @Override
781                 public Short deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
782                 {
783                     return endianUtil.decodeShort(buffer, offset);
784                 }
785             };
786 
787     /** Converter for int matrix. */
788     protected static final Serializer<int[][]> CONVERT_INT_MATRIX =
789             new BasicPrimitiveArrayOrMatrixSerializer<int[][]>(FieldTypes.INT_32_MATRIX, 4, "int_32_matrix", 2)
790             {
791                 @Override
792                 public int size(final int[][] matrix)
793                 {
794                     return 8 + getElementSize() * matrix.length * matrix[0].length;
795                 }
796 
797                 @Override
798                 public void serialize(final int[][] matrix, final byte[] buffer, final Pointer pointer,
799                         final EndianUtil endianUtil) throws SerializationException
800                 {
801                     int height = matrix.length;
802                     int width = matrix[0].length;
803                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
804                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
805                     for (int i = 0; i < height; i++)
806                     {
807                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
808                         for (int j = 0; j < width; j++)
809                         {
810                             endianUtil.encodeInt(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
811                         }
812                     }
813                 }
814 
815                 @Override
816                 public int[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
817                         throws SerializationException
818                 {
819                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
820                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
821                     int[][] result = new int[height][width];
822                     for (int i = 0; i < height; i++)
823                     {
824                         for (int j = 0; j < width; j++)
825                         {
826                             result[i][j] = endianUtil.decodeInt(buffer, pointer.getAndIncrement(getElementSize()));
827                         }
828                     }
829                     return result;
830                 }
831             };
832 
833     /** Converter for Integer matrix. */
834     protected static final Serializer<Integer[][]> CONVERT_INTEGER_MATRIX =
835             new ObjectMatrixSerializer<Integer>(FieldTypes.INT_32_MATRIX, 4, Integer.class, "Integer_32_matrix")
836             {
837                 @Override
838                 public void serializeElement(final Integer object, final byte[] buffer, final int offset,
839                         final EndianUtil endianUtil)
840                 {
841                     endianUtil.encodeInt(object, buffer, offset);
842                 }
843 
844                 @Override
845                 public Integer deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
846                 {
847                     return endianUtil.decodeInt(buffer, offset);
848                 }
849             };
850 
851     /** Converter for long matrix. */
852     protected static final Serializer<long[][]> CONVERT_LNG_MATRIX =
853             new BasicPrimitiveArrayOrMatrixSerializer<long[][]>(FieldTypes.LONG_64_MATRIX, 8, "long_64_matrix", 2)
854             {
855                 @Override
856                 public int size(final long[][] matrix)
857                 {
858                     return 8 + getElementSize() * matrix.length * matrix[0].length;
859                 }
860 
861                 @Override
862                 public void serialize(final long[][] matrix, final byte[] buffer, final Pointer pointer,
863                         final EndianUtil endianUtil) throws SerializationException
864                 {
865                     int height = matrix.length;
866                     int width = matrix[0].length;
867                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
868                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
869                     for (int i = 0; i < height; i++)
870                     {
871                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
872                         for (int j = 0; j < width; j++)
873                         {
874                             endianUtil.encodeLong(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
875                         }
876                     }
877                 }
878 
879                 @Override
880                 public long[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
881                         throws SerializationException
882                 {
883                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
884                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
885                     long[][] result = new long[height][width];
886                     for (int i = 0; i < height; i++)
887                     {
888                         for (int j = 0; j < width; j++)
889                         {
890                             result[i][j] = endianUtil.decodeLong(buffer, pointer.getAndIncrement(getElementSize()));
891                         }
892                     }
893                     return result;
894                 }
895             };
896 
897     /** Converter for Long matrix. */
898     protected static final Serializer<Long[][]> CONVERT_LONG_MATRIX =
899             new ObjectMatrixSerializer<Long>(FieldTypes.LONG_64_MATRIX, 8, Long.class, "Long_64_matrix")
900             {
901                 @Override
902                 public void serializeElement(final Long object, final byte[] buffer, final int offset,
903                         final EndianUtil endianUtil)
904                 {
905                     endianUtil.encodeLong(object, buffer, offset);
906                 }
907 
908                 @Override
909                 public Long deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
910                 {
911                     return endianUtil.decodeLong(buffer, offset);
912                 }
913             };
914 
915     /** Converter for float matrix. */
916     protected static final Serializer<float[][]> CONVERT_FLT_MATRIX =
917             new BasicPrimitiveArrayOrMatrixSerializer<float[][]>(FieldTypes.FLOAT_32_MATRIX, 4, "float_32_matrix", 2)
918             {
919                 @Override
920                 public int size(final float[][] matrix)
921                 {
922                     return 8 + getElementSize() * matrix.length * matrix[0].length;
923                 }
924 
925                 @Override
926                 public void serialize(final float[][] matrix, final byte[] buffer, final Pointer pointer,
927                         final EndianUtil endianUtil) throws SerializationException
928                 {
929                     int height = matrix.length;
930                     int width = matrix[0].length;
931                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
932                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
933                     for (int i = 0; i < height; i++)
934                     {
935                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
936                         for (int j = 0; j < width; j++)
937                         {
938                             endianUtil.encodeFloat(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
939                         }
940                     }
941                 }
942 
943                 @Override
944                 public float[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
945                         throws SerializationException
946                 {
947                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
948                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
949                     float[][] result = new float[height][width];
950                     for (int i = 0; i < height; i++)
951                     {
952                         for (int j = 0; j < width; j++)
953                         {
954                             result[i][j] = endianUtil.decodeFloat(buffer, pointer.getAndIncrement(getElementSize()));
955                         }
956                     }
957                     return result;
958                 }
959             };
960 
961     /** Converter for Float matrix. */
962     protected static final Serializer<Float[][]> CONVERT_FLOAT_MATRIX =
963             new ObjectMatrixSerializer<Float>(FieldTypes.FLOAT_32_MATRIX, 4, Float.class, "Float_32_matrix")
964             {
965                 @Override
966                 public void serializeElement(final Float object, final byte[] buffer, final int offset,
967                         final EndianUtil endianUtil)
968                 {
969                     endianUtil.encodeFloat(object, buffer, offset);
970                 }
971 
972                 @Override
973                 public Float deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
974                 {
975                     return endianUtil.decodeFloat(buffer, offset);
976                 }
977             };
978 
979     /** Converter for double matrix. */
980     protected static final Serializer<double[][]> CONVERT_DBL_MATRIX =
981             new BasicPrimitiveArrayOrMatrixSerializer<double[][]>(FieldTypes.DOUBLE_64_MATRIX, 8, "double_64_matrix", 2)
982             {
983                 @Override
984                 public int size(final double[][] matrix)
985                 {
986                     return 8 + getElementSize() * matrix.length * matrix[0].length;
987                 }
988 
989                 @Override
990                 public void serialize(final double[][] matrix, final byte[] buffer, final Pointer pointer,
991                         final EndianUtil endianUtil) throws SerializationException
992                 {
993                     int height = matrix.length;
994                     int width = matrix[0].length;
995                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
996                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
997                     for (int i = 0; i < height; i++)
998                     {
999                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
1000                         for (int j = 0; j < width; j++)
1001                         {
1002                             endianUtil.encodeDouble(matrix[i][j], buffer, pointer.getAndIncrement(getElementSize()));
1003                         }
1004                     }
1005                 }
1006 
1007                 @Override
1008                 public double[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1009                         throws SerializationException
1010                 {
1011                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1012                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1013                     double[][] result = new double[height][width];
1014                     for (int i = 0; i < height; i++)
1015                     {
1016                         for (int j = 0; j < width; j++)
1017                         {
1018                             result[i][j] = endianUtil.decodeDouble(buffer, pointer.getAndIncrement(getElementSize()));
1019                         }
1020                     }
1021                     return result;
1022                 }
1023             };
1024 
1025     /** Converter for Double matrix. */
1026     protected static final Serializer<Double[][]> CONVERT_DOUBLE_MATRIX =
1027             new ObjectMatrixSerializer<Double>(FieldTypes.DOUBLE_64_MATRIX, 8, Double.class, "Double_64_matrix")
1028             {
1029                 @Override
1030                 public void serializeElement(final Double object, final byte[] buffer, final int offset,
1031                         final EndianUtil endianUtil)
1032                 {
1033                     endianUtil.encodeDouble(object, buffer, offset);
1034                 }
1035 
1036                 @Override
1037                 public Double deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
1038                 {
1039                     return endianUtil.decodeDouble(buffer, offset);
1040                 }
1041             };
1042 
1043     /** Converter for boolean matrix. */
1044     protected static final Serializer<boolean[][]> CONVERT_BOOL_MATRIX =
1045             new BasicPrimitiveArrayOrMatrixSerializer<boolean[][]>(FieldTypes.BOOLEAN_8_MATRIX, 1, "boolean_8_matrix", 2)
1046             {
1047                 @Override
1048                 public int size(final boolean[][] matrix)
1049                 {
1050                     return 8 + getElementSize() * matrix.length * matrix[0].length;
1051                 }
1052 
1053                 @Override
1054                 public void serialize(final boolean[][] matrix, final byte[] buffer, final Pointer pointer,
1055                         final EndianUtil endianUtil) throws SerializationException
1056                 {
1057                     int height = matrix.length;
1058                     int width = matrix[0].length;
1059                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
1060                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
1061                     for (int i = 0; i < height; i++)
1062                     {
1063                         Throw.when(matrix[i].length != width, SerializationException.class, "Jagged matrix is not allowed");
1064                         for (int j = 0; j < width; j++)
1065                         {
1066                             buffer[pointer.getAndIncrement(getElementSize())] = (byte) (matrix[i][j] ? 1 : 0);
1067                         }
1068                     }
1069                 }
1070 
1071                 @Override
1072                 public boolean[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1073                         throws SerializationException
1074                 {
1075                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1076                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1077                     boolean[][] result = new boolean[height][width];
1078                     for (int i = 0; i < height; i++)
1079                     {
1080                         for (int j = 0; j < width; j++)
1081                         {
1082                             result[i][j] = buffer[pointer.getAndIncrement(getElementSize())] != 0;
1083                         }
1084                     }
1085                     return result;
1086                 }
1087             };
1088 
1089     /** Converter for Boolean matrix. */
1090     protected static final Serializer<Boolean[][]> CONVERT_BOOLEAN_MATRIX =
1091             new ObjectMatrixSerializer<Boolean>(FieldTypes.BOOLEAN_8_MATRIX, 1, Boolean.class, "Boolean_8_matrix")
1092             {
1093                 @Override
1094                 public void serializeElement(final Boolean object, final byte[] buffer, final int offset,
1095                         final EndianUtil endianUtil)
1096                 {
1097                     buffer[offset] = (byte) (object ? 1 : 0);
1098                 }
1099 
1100                 @Override
1101                 public Boolean deSerializeElement(final byte[] buffer, final int offset, final EndianUtil endianUtil)
1102                 {
1103                     return buffer[offset] != 0;
1104                 }
1105             };
1106 
1107     /** Converter for descendants of FloatScalar. */
1108     @SuppressWarnings({"rawtypes", "unchecked"})
1109     protected static final Serializer<FloatScalar<?, ?>> CONVERT_DJUNITS_FLOAT_SCALAR = new FloatScalarSerializer();
1110 
1111     /** Converter for descendants of DoubleScalar. */
1112     @SuppressWarnings({"rawtypes", "unchecked"})
1113     protected static final Serializer<DoubleScalar<?, ?>> CONVERT_DJUNITS_DOUBLE_SCALAR = new DoubleScalarSerializer();
1114 
1115     /** Converter for descendants of FloatVector. */
1116     @SuppressWarnings({"rawtypes", "unchecked"})
1117     protected static final Serializer<FloatVector<?, ?, ?>> CONVERT_DJUNITS_FLOAT_VECTOR = new FloatVectorSerializer();
1118 
1119     /** Converter for descendants of DoubleVector. */
1120     @SuppressWarnings({"rawtypes", "unchecked"})
1121     protected static final Serializer<DoubleVector<?, ?, ?>> CONVERT_DJUNITS_DOUBLE_VECTOR = new DoubleVectorSerializer();
1122 
1123     /** Converter for descendants of FloatMatrix. */
1124     @SuppressWarnings({"rawtypes", "unchecked"})
1125     protected static final Serializer<FloatMatrix<?, ?, ?, ?>> CONVERT_DJUNITS_FLOAT_MATRIX = new FloatMatrixSerializer();
1126 
1127     /** Converter for descendants of DoubleMatrix. */
1128     @SuppressWarnings({"rawtypes", "unchecked"})
1129     protected static final Serializer<DoubleMatrix> CONVERT_DJUNITS_DOUBLE_MATRIX = new DoubleMatrixSerializer();
1130 
1131     /** Serializer for array of FloatVector. Each FloatVector must have same size. */
1132     @SuppressWarnings({"rawtypes", "unchecked"})
1133     protected static final Serializer<FloatVector[]> CONVERT_FLOAT_UNIT_COLUMN_VECTOR_ARRAY = new FloatVectorArraySerializer();
1134 
1135     /** Serializer for array of DoubleVector. Each DoubleVector must have same size. */
1136     @SuppressWarnings({"rawtypes", "unchecked"})
1137     protected static final Serializer<DoubleVector[]> CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY =
1138             new DoubleVectorArraySerializer();
1139 
1140     /** Converter for array of SerializebleObject using UTF16 for strings and characters. */
1141     protected static final Serializer<SerializableObject<?>[]> COMPOUND_ARRAY_SERIALIZER_UTF16 =
1142             new ObjectSerializer<SerializableObject<?>[]>((byte) 120, "Compound")
1143             {
1144 
1145                 @SuppressWarnings({"unchecked", "rawtypes"})
1146                 @Override
1147                 public int size(final SerializableObject<?>[] objects) throws SerializationException
1148                 {
1149                     int result = 4 + 4;
1150                     SerializableObject<?> so = objects[0];
1151                     Object[] objectArray = so.exportAsList().toArray();
1152                     Serializer[] serializers = TypedMessage.buildEncoderList(false, objectArray);
1153                     result += serializers.length;
1154                     for (int i = 0; i < objectArray.length; i++)
1155                     {
1156                         result += objects.length * serializers[i].size(objectArray[i]);
1157                     }
1158                     return result;
1159                 }
1160 
1161                 @SuppressWarnings({"unchecked", "rawtypes"})
1162                 @Override
1163                 public void serialize(final SerializableObject<?>[] objects, final byte[] buffer, final Pointer pointer,
1164                         final EndianUtil endianUtil) throws SerializationException
1165                 {
1166                     SerializableObject<?> so = objects[0];
1167                     Object[] objectArray = so.exportAsList().toArray();
1168                     endianUtil.encodeInt(objects.length, buffer, pointer.getAndIncrement(4));
1169                     endianUtil.encodeInt(objectArray.length, buffer, pointer.getAndIncrement(4));
1170                     Serializer[] serializers = TypedMessage.buildEncoderList(false, objectArray);
1171                     for (int i = 0; i < objectArray.length; i++)
1172                     {
1173                         buffer[pointer.getAndIncrement(1)] = serializers[i].fieldType();
1174                     }
1175                     for (int i = 0; i < objects.length; i++)
1176                     {
1177                         List<Object> row = objects[i].exportAsList();
1178                         Throw.when(row.size() != objectArray.length, SerializationException.class,
1179                                 "List in row %d has %d elements which differs from the %d elements in row 0", i, row.size(),
1180                                 objectArray.length);
1181                         for (int j = 0; j < row.size(); j++)
1182                         {
1183                             serializers[j].serialize(row.get(j), buffer, pointer, endianUtil);
1184                         }
1185                     }
1186                 }
1187 
1188                 @Override
1189                 public SerializableObject<?>[] deSerialize(final byte[] buffer, final Pointer pointer,
1190                         final EndianUtil endianUtil) throws SerializationException
1191                 {
1192                     int arraySize = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1193                     int fieldCount = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1194                     Serializer<?>[] deSerializers = new Serializer[fieldCount];
1195                     for (int i = 0; i < fieldCount; i++)
1196                     {
1197                         Byte key = buffer[pointer.getAndIncrement(1)];
1198                         Serializer<?> deSerializer = PRIMITIVE_DATA_DECODERS.get(key);
1199                         Throw.whenNull(SerializationException.class, "No decoder for %d", key);
1200                         deSerializers[i] = deSerializer;
1201                     }
1202                     MinimalSerializableObject[] result = new MinimalSerializableObject[arraySize];
1203                     for (int i = 0; i < arraySize; i++)
1204                     {
1205                         List<Object> element = new ArrayList<>();
1206                         for (int j = 0; j < fieldCount; j++)
1207                         {
1208                             element.add(deSerializers[j].deSerialize(buffer, pointer, endianUtil));
1209                         }
1210                         result[i] = new MinimalSerializableObject(element);
1211                     }
1212                     return result;
1213                 }
1214             };
1215 
1216     /** Converter for array of SerializebleObject using UTF8 for strings and characters. */
1217     protected static final Serializer<SerializableObject<?>[]> COMPOUND_ARRAY_SERIALIZER_UTF8 =
1218             new ObjectSerializer<SerializableObject<?>[]>((byte) 121, "Compound")
1219             {
1220 
1221                 @SuppressWarnings({"unchecked", "rawtypes"})
1222                 @Override
1223                 public int size(final SerializableObject<?>[] objects) throws SerializationException
1224                 {
1225                     int result = 4 + 4;
1226                     SerializableObject<?> so = objects[0];
1227                     Object[] objectArray = so.exportAsList().toArray();
1228                     Serializer[] serializers = TypedMessage.buildEncoderList(true, objectArray);
1229                     result += serializers.length;
1230                     for (int i = 0; i < objectArray.length; i++)
1231                     {
1232                         result += objects.length * serializers[i].size(objectArray[i]);
1233                     }
1234                     return result;
1235                 }
1236 
1237                 @SuppressWarnings({"unchecked", "rawtypes"})
1238                 @Override
1239                 public void serialize(final SerializableObject<?>[] objects, final byte[] buffer, final Pointer pointer,
1240                         final EndianUtil endianUtil) throws SerializationException
1241                 {
1242                     SerializableObject<?> so = objects[0];
1243                     Object[] objectArray = so.exportAsList().toArray();
1244                     endianUtil.encodeInt(objects.length, buffer, pointer.getAndIncrement(4));
1245                     endianUtil.encodeInt(objectArray.length, buffer, pointer.getAndIncrement(4));
1246                     Serializer[] serializers = TypedMessage.buildEncoderList(true, objectArray);
1247                     for (int i = 0; i < objectArray.length; i++)
1248                     {
1249                         buffer[pointer.getAndIncrement(1)] = serializers[i].fieldType();
1250                     }
1251                     for (int i = 0; i < objects.length; i++)
1252                     {
1253                         List<Object> row = objects[i].exportAsList();
1254                         Throw.when(row.size() != objectArray.length, SerializationException.class,
1255                                 "List in row %d has %d elements which differs from the %d elements in row 0", i, row.size(),
1256                                 objectArray.length);
1257                         for (int j = 0; j < row.size(); j++)
1258                         {
1259                             serializers[j].serialize(row.get(j), buffer, pointer, endianUtil);
1260                         }
1261                     }
1262                 }
1263 
1264                 @Override
1265                 public SerializableObject<?>[] deSerialize(final byte[] buffer, final Pointer pointer,
1266                         final EndianUtil endianUtil) throws SerializationException
1267                 {
1268                     int arraySize = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1269                     int fieldCount = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1270                     Serializer<?>[] deSerializers = new Serializer[fieldCount];
1271                     for (int i = 0; i < fieldCount; i++)
1272                     {
1273                         Byte key = buffer[pointer.getAndIncrement(1)];
1274                         Serializer<?> deSerializer = PRIMITIVE_DATA_DECODERS.get(key);
1275                         Throw.whenNull(SerializationException.class, "No decoder for %d", key);
1276                         deSerializers[i] = deSerializer;
1277                     }
1278                     MinimalSerializableObject[] result = new MinimalSerializableObject[arraySize];
1279                     for (int i = 0; i < arraySize; i++)
1280                     {
1281                         List<Object> element = new ArrayList<>();
1282                         for (int j = 0; j < fieldCount; j++)
1283                         {
1284                             element.add(deSerializers[j].deSerialize(buffer, pointer, endianUtil));
1285                         }
1286                         result[i] = new MinimalSerializableObject(element);
1287                     }
1288                     return result;
1289                 }
1290             };
1291 
1292     /** Converter for String UTF-8 array. */
1293     protected static final Serializer<String[]> CONVERT_STRING8_ARRAY =
1294             new StringArraySerializer(FieldTypes.STRING_UTF8_ARRAY, "String_8_array")
1295             {
1296                 @Override
1297                 public int size(final String[] stringArray)
1298                 {
1299                     int size = 4;
1300                     for (String string : stringArray)
1301                     {
1302                         size += 4 + string.getBytes(UTF8).length;
1303                     }
1304                     return size;
1305                 }
1306 
1307                 @Override
1308                 public void serialize(final String[] stringArray, final byte[] buffer, final Pointer pointer,
1309                         final EndianUtil endianUtil)
1310                 {
1311                     endianUtil.encodeInt(stringArray.length, buffer, pointer.getAndIncrement(4));
1312                     for (String string : stringArray)
1313                     {
1314                         byte[] s = string.getBytes(UTF8);
1315                         endianUtil.encodeInt(s.length, buffer, pointer.getAndIncrement(4));
1316                         for (byte b : s)
1317                         {
1318                             buffer[pointer.getAndIncrement(1)] = b;
1319                         }
1320                     }
1321                 }
1322 
1323                 @Override
1324                 public String[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1325                         throws SerializationException
1326                 {
1327                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1328                     String[] result = new String[size];
1329                     for (int i = 0; i < size; i++)
1330                     {
1331                         int bytesUsed = endianUtil.decodeInt(buffer, pointer.get());
1332                         result[i] = endianUtil.decodeUTF8String(buffer, pointer.get());
1333                         pointer.getAndIncrement(4 + bytesUsed);
1334                     }
1335                     return result;
1336                 }
1337 
1338             };
1339 
1340     /** Converter for String UTF-16 array. */
1341     protected static final Serializer<String[]> CONVERT_STRING16_ARRAY =
1342             new StringArraySerializer(FieldTypes.STRING_UTF16_ARRAY, "String_16_array")
1343             {
1344                 @Override
1345                 public int size(final String[] stringArray)
1346                 {
1347                     int size = 4;
1348                     for (String string : stringArray)
1349                     {
1350                         size += 4 + string.getBytes(UTF16).length;
1351                     }
1352                     return size;
1353                 }
1354 
1355                 @Override
1356                 public void serialize(final String[] stringArray, final byte[] buffer, final Pointer pointer,
1357                         final EndianUtil endianUtil)
1358                 {
1359                     endianUtil.encodeInt(stringArray.length, buffer, pointer.getAndIncrement(4));
1360                     for (String string : stringArray)
1361                     {
1362                         // Note that according to https://stackoverflow.com/questions/74887443, String.length returns
1363                         // the number of code units (i.e. the number of 16-bit char values) needed to make up the String
1364                         // and not the number of Unicode codepoints.
1365                         char[] chars = new char[string.length()];
1366                         string.getChars(0, chars.length, chars, 0);
1367                         endianUtil.encodeInt(chars.length, buffer, pointer.getAndIncrement(4));
1368                         for (char c : chars)
1369                         {
1370                             endianUtil.encodeChar(c, buffer, pointer.getAndIncrement(2));
1371                         }
1372                     }
1373                 }
1374 
1375                 @Override
1376                 public String[] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1377                 {
1378                     int size = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1379                     String[] result = new String[size];
1380                     for (int i = 0; i < size; i++)
1381                     {
1382                         result[i] = endianUtil.decodeUTF16String(buffer, pointer.get());
1383                         pointer.getAndIncrement(4 + result[i].length() * 2);
1384                     }
1385                     return result;
1386                 }
1387             };
1388 
1389     /** Converter for String UTF-8 matrix. */
1390     protected static final Serializer<String[][]> CONVERT_STRING8_MATRIX =
1391             new StringMatrixSerializer(FieldTypes.STRING_UTF8_MATRIX, "String_8_matrix")
1392             {
1393                 @Override
1394                 public int size(final String[][] stringMatrix)
1395                 {
1396                     int size = 8;
1397                     for (String[] stringArray : stringMatrix)
1398                     {
1399                         for (String string : stringArray)
1400                         {
1401                             size += 4 + string.getBytes(UTF8).length;
1402                         }
1403                     }
1404                     return size;
1405                 }
1406 
1407                 @Override
1408                 public void serialize(final String[][] stringMatrix, final byte[] buffer, final Pointer pointer,
1409                         final EndianUtil endianUtil) throws SerializationException
1410                 {
1411                     int height = stringMatrix.length;
1412                     int width = stringMatrix[0].length;
1413                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
1414                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
1415                     for (int i = 0; i < height; i++)
1416                     {
1417                         Throw.when(stringMatrix[i].length != width, SerializationException.class,
1418                                 "Jagged matrix is not allowed");
1419                         for (int j = 0; j < width; j++)
1420                         {
1421                             byte[] s = stringMatrix[i][j].getBytes(UTF8);
1422                             endianUtil.encodeInt(s.length, buffer, pointer.getAndIncrement(4));
1423                             for (byte b : s)
1424                             {
1425                                 buffer[pointer.getAndIncrement(1)] = b;
1426                             }
1427                         }
1428                     }
1429                 }
1430 
1431                 @Override
1432                 public String[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1433                         throws SerializationException
1434                 {
1435                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1436                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1437                     String[][] result = new String[height][width];
1438                     for (int i = 0; i < height; i++)
1439                     {
1440                         for (int j = 0; j < width; j++)
1441                         {
1442                             int bytesUsed = endianUtil.decodeInt(buffer, pointer.get());
1443                             result[i][j] = endianUtil.decodeUTF8String(buffer, pointer.get());
1444                             pointer.getAndIncrement(4 + bytesUsed);
1445                         }
1446                     }
1447                     return result;
1448                 }
1449             };
1450 
1451     /** Converter for String UTF-16 matrix. */
1452     protected static final Serializer<String[][]> CONVERT_STRING16_MATRIX =
1453             new StringMatrixSerializer(FieldTypes.STRING_UTF16_MATRIX, "String_16_matrix")
1454             {
1455                 @Override
1456                 public int size(final String[][] stringMatrix)
1457                 {
1458                     int size = 8;
1459                     for (String[] stringArray : stringMatrix)
1460                     {
1461                         for (String string : stringArray)
1462                         {
1463                             size += 4 + string.getBytes(UTF16).length;
1464                         }
1465                     }
1466                     return size;
1467                 }
1468 
1469                 @Override
1470                 public void serialize(final String[][] stringMatrix, final byte[] buffer, final Pointer pointer,
1471                         final EndianUtil endianUtil) throws SerializationException
1472                 {
1473                     int height = stringMatrix.length;
1474                     int width = stringMatrix[0].length;
1475                     endianUtil.encodeInt(height, buffer, pointer.getAndIncrement(4));
1476                     endianUtil.encodeInt(width, buffer, pointer.getAndIncrement(4));
1477                     for (int i = 0; i < height; i++)
1478                     {
1479                         Throw.when(stringMatrix[i].length != width, SerializationException.class,
1480                                 "Jagged matrix is not allowed");
1481                         for (int j = 0; j < width; j++)
1482                         {
1483                             // Note that according to https://stackoverflow.com/questions/74887443, String.length returns
1484                             // the number of code units (i.e. the number of 16-bit char values) needed to make up the String
1485                             // and not the number of Unicode codepoints.
1486                             char[] chars = new char[stringMatrix[i][j].length()];
1487                             stringMatrix[i][j].getChars(0, chars.length, chars, 0);
1488                             endianUtil.encodeInt(chars.length, buffer, pointer.getAndIncrement(4));
1489                             for (char c : chars)
1490                             {
1491                                 endianUtil.encodeChar(c, buffer, pointer.getAndIncrement(2));
1492                             }
1493                         }
1494                     }
1495                 }
1496 
1497                 @Override
1498                 public String[][] deSerialize(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1499                 {
1500                     int height = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1501                     int width = endianUtil.decodeInt(buffer, pointer.getAndIncrement(4));
1502                     String[][] result = new String[height][width];
1503                     for (int i = 0; i < height; i++)
1504                     {
1505                         for (int j = 0; j < width; j++)
1506                         {
1507                             result[i][j] = endianUtil.decodeUTF16String(buffer, pointer.get());
1508                             pointer.getAndIncrement(4 + result[i][j].length() * 2);
1509                         }
1510                     }
1511                     return result;
1512 
1513                 }
1514             };
1515 
1516     static
1517     {
1518         ENCODERS.put(Byte.class, CONVERT_BYTE);
1519         ENCODERS.put(byte.class, CONVERT_BYTE);
1520         ENCODERS.put(Short.class, CONVERT_SHORT);
1521         ENCODERS.put(short.class, CONVERT_SHORT);
1522         ENCODERS.put(Integer.class, CONVERT_INTEGER);
1523         ENCODERS.put(int.class, CONVERT_INTEGER);
1524         ENCODERS.put(Long.class, CONVERT_LONG);
1525         ENCODERS.put(long.class, CONVERT_LONG);
1526         ENCODERS.put(Float.class, CONVERT_FLOAT);
1527         ENCODERS.put(float.class, CONVERT_FLOAT);
1528         ENCODERS.put(Double.class, CONVERT_DOUBLE);
1529         ENCODERS.put(double.class, CONVERT_DOUBLE);
1530         ENCODERS.put(Boolean.class, CONVERT_BOOLEAN);
1531         ENCODERS.put(boolean.class, CONVERT_BOOLEAN);
1532         ENCODERS.put(Byte[].class, CONVERT_BYTE_ARRAY);
1533         ENCODERS.put(byte[].class, CONVERT_BT_ARRAY);
1534         ENCODERS.put(Short[].class, CONVERT_SHORT_ARRAY);
1535         ENCODERS.put(short[].class, CONVERT_SHRT_ARRAY);
1536         ENCODERS.put(Integer[].class, CONVERT_INTEGER_ARRAY);
1537         ENCODERS.put(int[].class, CONVERT_INT_ARRAY);
1538         ENCODERS.put(Long[].class, CONVERT_LONG_ARRAY);
1539         ENCODERS.put(long[].class, CONVERT_LNG_ARRAY);
1540         ENCODERS.put(Float[].class, CONVERT_FLOAT_ARRAY);
1541         ENCODERS.put(float[].class, CONVERT_FLT_ARRAY);
1542         ENCODERS.put(Double[].class, CONVERT_DOUBLE_ARRAY);
1543         ENCODERS.put(double[].class, CONVERT_DBL_ARRAY);
1544         ENCODERS.put(Boolean[].class, CONVERT_BOOLEAN_ARRAY);
1545         ENCODERS.put(boolean[].class, CONVERT_BOOL_ARRAY);
1546         ENCODERS.put(Byte[][].class, CONVERT_BYTE_MATRIX);
1547         ENCODERS.put(byte[][].class, CONVERT_BT_MATRIX);
1548         ENCODERS.put(Short[][].class, CONVERT_SHORT_MATRIX);
1549         ENCODERS.put(short[][].class, CONVERT_SHRT_MATRIX);
1550         ENCODERS.put(Integer[][].class, CONVERT_INTEGER_MATRIX);
1551         ENCODERS.put(int[][].class, CONVERT_INT_MATRIX);
1552         ENCODERS.put(Long[][].class, CONVERT_LONG_MATRIX);
1553         ENCODERS.put(long[][].class, CONVERT_LNG_MATRIX);
1554         ENCODERS.put(Float[][].class, CONVERT_FLOAT_MATRIX);
1555         ENCODERS.put(float[][].class, CONVERT_FLT_MATRIX);
1556         ENCODERS.put(Double[][].class, CONVERT_DOUBLE_MATRIX);
1557         ENCODERS.put(double[][].class, CONVERT_DBL_MATRIX);
1558         ENCODERS.put(Boolean[][].class, CONVERT_BOOLEAN_MATRIX);
1559         ENCODERS.put(boolean[][].class, CONVERT_BOOL_MATRIX);
1560 
1561         PRIMITIVE_DATA_DECODERS.put(CONVERT_BYTE.fieldType(), CONVERT_BYTE);
1562         PRIMITIVE_DATA_DECODERS.put(CONVERT_CHARACTER8.fieldType(), CONVERT_CHARACTER8);
1563         PRIMITIVE_DATA_DECODERS.put(CONVERT_CHARACTER16.fieldType(), CONVERT_CHARACTER16);
1564         PRIMITIVE_DATA_DECODERS.put(CONVERT_SHORT.fieldType(), CONVERT_SHORT);
1565         PRIMITIVE_DATA_DECODERS.put(CONVERT_INTEGER.fieldType(), CONVERT_INTEGER);
1566         PRIMITIVE_DATA_DECODERS.put(CONVERT_LONG.fieldType(), CONVERT_LONG);
1567         PRIMITIVE_DATA_DECODERS.put(CONVERT_FLOAT.fieldType(), CONVERT_FLOAT);
1568         PRIMITIVE_DATA_DECODERS.put(CONVERT_DOUBLE.fieldType(), CONVERT_DOUBLE);
1569         PRIMITIVE_DATA_DECODERS.put(CONVERT_BOOLEAN.fieldType(), CONVERT_BOOLEAN);
1570         PRIMITIVE_DATA_DECODERS.put(CONVERT_STRING8.fieldType(), CONVERT_STRING8);
1571         PRIMITIVE_DATA_DECODERS.put(CONVERT_STRING16.fieldType(), CONVERT_STRING16);
1572         PRIMITIVE_DATA_DECODERS.put(CONVERT_BT_ARRAY.fieldType(), CONVERT_BT_ARRAY);
1573         PRIMITIVE_DATA_DECODERS.put(CONVERT_SHRT_ARRAY.fieldType(), CONVERT_SHRT_ARRAY);
1574         PRIMITIVE_DATA_DECODERS.put(CONVERT_INT_ARRAY.fieldType(), CONVERT_INT_ARRAY);
1575         PRIMITIVE_DATA_DECODERS.put(CONVERT_LNG_ARRAY.fieldType(), CONVERT_LNG_ARRAY);
1576         PRIMITIVE_DATA_DECODERS.put(CONVERT_FLT_ARRAY.fieldType(), CONVERT_FLT_ARRAY);
1577         PRIMITIVE_DATA_DECODERS.put(CONVERT_DBL_ARRAY.fieldType(), CONVERT_DBL_ARRAY);
1578         PRIMITIVE_DATA_DECODERS.put(CONVERT_BOOL_ARRAY.fieldType(), CONVERT_BOOL_ARRAY);
1579         PRIMITIVE_DATA_DECODERS.put(CONVERT_BT_MATRIX.fieldType(), CONVERT_BT_MATRIX);
1580         PRIMITIVE_DATA_DECODERS.put(CONVERT_SHRT_MATRIX.fieldType(), CONVERT_SHRT_MATRIX);
1581         PRIMITIVE_DATA_DECODERS.put(CONVERT_INT_MATRIX.fieldType(), CONVERT_INT_MATRIX);
1582         PRIMITIVE_DATA_DECODERS.put(CONVERT_LNG_MATRIX.fieldType(), CONVERT_LNG_MATRIX);
1583         PRIMITIVE_DATA_DECODERS.put(CONVERT_FLT_MATRIX.fieldType(), CONVERT_FLT_MATRIX);
1584         PRIMITIVE_DATA_DECODERS.put(CONVERT_DBL_MATRIX.fieldType(), CONVERT_DBL_MATRIX);
1585         PRIMITIVE_DATA_DECODERS.put(CONVERT_BOOL_MATRIX.fieldType(), CONVERT_BOOL_MATRIX);
1586         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_FLOAT_SCALAR.fieldType(), CONVERT_DJUNITS_FLOAT_SCALAR);
1587         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_DOUBLE_SCALAR.fieldType(), CONVERT_DJUNITS_DOUBLE_SCALAR);
1588         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_FLOAT_VECTOR.fieldType(), CONVERT_DJUNITS_FLOAT_VECTOR);
1589         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_DOUBLE_VECTOR.fieldType(), CONVERT_DJUNITS_DOUBLE_VECTOR);
1590         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_FLOAT_MATRIX.fieldType(), CONVERT_DJUNITS_FLOAT_MATRIX);
1591         PRIMITIVE_DATA_DECODERS.put(CONVERT_DJUNITS_DOUBLE_MATRIX.fieldType(), CONVERT_DJUNITS_DOUBLE_MATRIX);
1592         PRIMITIVE_DATA_DECODERS.put(COMPOUND_ARRAY_SERIALIZER_UTF16.fieldType(), COMPOUND_ARRAY_SERIALIZER_UTF16);
1593         PRIMITIVE_DATA_DECODERS.put(COMPOUND_ARRAY_SERIALIZER_UTF8.fieldType(), COMPOUND_ARRAY_SERIALIZER_UTF8);
1594         PRIMITIVE_DATA_DECODERS.put(CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY.fieldType(),
1595                 CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY);
1596         PRIMITIVE_DATA_DECODERS.put(CONVERT_FLOAT_UNIT_COLUMN_VECTOR_ARRAY.fieldType(), CONVERT_FLOAT_UNIT_COLUMN_VECTOR_ARRAY);
1597         PRIMITIVE_DATA_DECODERS.put(CONVERT_STRING8_ARRAY.fieldType(), CONVERT_STRING8_ARRAY);
1598         PRIMITIVE_DATA_DECODERS.put(CONVERT_STRING16_ARRAY.fieldType(), CONVERT_STRING16_ARRAY);
1599         PRIMITIVE_DATA_DECODERS.put(CONVERT_STRING8_MATRIX.fieldType(), CONVERT_STRING8_MATRIX);
1600         PRIMITIVE_DATA_DECODERS.put(CONVERT_STRING16_MATRIX.fieldType(), CONVERT_STRING16_MATRIX);
1601 
1602         OBJECT_DECODERS.put(CONVERT_BYTE.fieldType(), CONVERT_BYTE);
1603         OBJECT_DECODERS.put(CONVERT_CHARACTER8.fieldType(), CONVERT_CHARACTER8);
1604         OBJECT_DECODERS.put(CONVERT_CHARACTER16.fieldType(), CONVERT_CHARACTER16);
1605         OBJECT_DECODERS.put(CONVERT_SHORT.fieldType(), CONVERT_SHORT);
1606         OBJECT_DECODERS.put(CONVERT_INTEGER.fieldType(), CONVERT_INTEGER);
1607         OBJECT_DECODERS.put(CONVERT_LONG.fieldType(), CONVERT_LONG);
1608         OBJECT_DECODERS.put(CONVERT_FLOAT.fieldType(), CONVERT_FLOAT);
1609         OBJECT_DECODERS.put(CONVERT_DOUBLE.fieldType(), CONVERT_DOUBLE);
1610         OBJECT_DECODERS.put(CONVERT_BOOLEAN.fieldType(), CONVERT_BOOLEAN);
1611         OBJECT_DECODERS.put(CONVERT_STRING8.fieldType(), CONVERT_STRING8);
1612         OBJECT_DECODERS.put(CONVERT_STRING16.fieldType(), CONVERT_STRING16);
1613         OBJECT_DECODERS.put(CONVERT_BYTE_ARRAY.fieldType(), CONVERT_BYTE_ARRAY);
1614         OBJECT_DECODERS.put(CONVERT_SHORT_ARRAY.fieldType(), CONVERT_SHORT_ARRAY);
1615         OBJECT_DECODERS.put(CONVERT_INTEGER_ARRAY.fieldType(), CONVERT_INTEGER_ARRAY);
1616         OBJECT_DECODERS.put(CONVERT_LONG_ARRAY.fieldType(), CONVERT_LONG_ARRAY);
1617         OBJECT_DECODERS.put(CONVERT_FLOAT_ARRAY.fieldType(), CONVERT_FLOAT_ARRAY);
1618         OBJECT_DECODERS.put(CONVERT_DOUBLE_ARRAY.fieldType(), CONVERT_DOUBLE_ARRAY);
1619         OBJECT_DECODERS.put(CONVERT_BOOLEAN_ARRAY.fieldType(), CONVERT_BOOLEAN_ARRAY);
1620         OBJECT_DECODERS.put(CONVERT_BYTE_MATRIX.fieldType(), CONVERT_BYTE_MATRIX);
1621         OBJECT_DECODERS.put(CONVERT_SHORT_MATRIX.fieldType(), CONVERT_SHORT_MATRIX);
1622         OBJECT_DECODERS.put(CONVERT_INTEGER_MATRIX.fieldType(), CONVERT_INTEGER_MATRIX);
1623         OBJECT_DECODERS.put(CONVERT_LONG_MATRIX.fieldType(), CONVERT_LONG_MATRIX);
1624         OBJECT_DECODERS.put(CONVERT_FLOAT_MATRIX.fieldType(), CONVERT_FLOAT_MATRIX);
1625         OBJECT_DECODERS.put(CONVERT_DOUBLE_MATRIX.fieldType(), CONVERT_DOUBLE_MATRIX);
1626         OBJECT_DECODERS.put(CONVERT_BOOLEAN_MATRIX.fieldType(), CONVERT_BOOLEAN_MATRIX);
1627         OBJECT_DECODERS.put(CONVERT_DJUNITS_FLOAT_SCALAR.fieldType(), CONVERT_DJUNITS_FLOAT_SCALAR);
1628         OBJECT_DECODERS.put(CONVERT_DJUNITS_DOUBLE_SCALAR.fieldType(), CONVERT_DJUNITS_DOUBLE_SCALAR);
1629         OBJECT_DECODERS.put(CONVERT_DJUNITS_FLOAT_VECTOR.fieldType(), CONVERT_DJUNITS_FLOAT_VECTOR);
1630         OBJECT_DECODERS.put(CONVERT_DJUNITS_DOUBLE_VECTOR.fieldType(), CONVERT_DJUNITS_DOUBLE_VECTOR);
1631         OBJECT_DECODERS.put(CONVERT_DJUNITS_FLOAT_MATRIX.fieldType(), CONVERT_DJUNITS_FLOAT_MATRIX);
1632         OBJECT_DECODERS.put(CONVERT_DJUNITS_DOUBLE_MATRIX.fieldType(), CONVERT_DJUNITS_DOUBLE_MATRIX);
1633         OBJECT_DECODERS.put(COMPOUND_ARRAY_SERIALIZER_UTF16.fieldType(), COMPOUND_ARRAY_SERIALIZER_UTF16);
1634         OBJECT_DECODERS.put(COMPOUND_ARRAY_SERIALIZER_UTF8.fieldType(), COMPOUND_ARRAY_SERIALIZER_UTF8);
1635         OBJECT_DECODERS.put(CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY.fieldType(), CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY);
1636         OBJECT_DECODERS.put(CONVERT_FLOAT_UNIT_COLUMN_VECTOR_ARRAY.fieldType(), CONVERT_FLOAT_UNIT_COLUMN_VECTOR_ARRAY);
1637         OBJECT_DECODERS.put(CONVERT_STRING8_ARRAY.fieldType(), CONVERT_STRING8_ARRAY);
1638         OBJECT_DECODERS.put(CONVERT_STRING16_ARRAY.fieldType(), CONVERT_STRING16_ARRAY);
1639         OBJECT_DECODERS.put(CONVERT_STRING8_MATRIX.fieldType(), CONVERT_STRING8_MATRIX);
1640         OBJECT_DECODERS.put(CONVERT_STRING16_MATRIX.fieldType(), CONVERT_STRING16_MATRIX);
1641     }
1642 
1643     /** the UTF-8 charset. */
1644     protected static final Charset UTF8 = Charset.forName("UTF-8");
1645 
1646     /** the UTF-16 charset, big endian variant. */
1647     protected static final Charset UTF16 = Charset.forName("UTF-16BE");
1648 
1649     /**
1650      * Encode the object into a byte[] message. Use UTF8 for potential characters and Strings.
1651      * @param endianUtil encoder to use for multi-byte values
1652      * @param content the objects to encode
1653      * @return the zeroMQ message to send as a byte array
1654      * @throws SerializationException on unknown data type
1655      */
1656     public static byte[] encode(final EndianUtil endianUtil, final Object content) throws SerializationException
1657     {
1658         return encode(true, endianUtil, content);
1659     }
1660 
1661     /**
1662      * Encode the object into a byte[] message. Use UTF8 for the characters and for the String.
1663      * @param endianUtil encoder to use for multi-byte values
1664      * @param content the objects to encode
1665      * @return the zeroMQ message to send as a byte array
1666      * @throws SerializationException on unknown data type
1667      */
1668     public static byte[] encodeUTF8(final EndianUtil endianUtil, final Object content) throws SerializationException
1669     {
1670         return encode(true, endianUtil, content);
1671     }
1672 
1673     /**
1674      * Encode the object array into a byte[] message. Use UTF16 for the characters and for the String.
1675      * @param endianUtil encoder for multi-byte values
1676      * @param content the objects to encode
1677      * @return the zeroMQ message to send as a byte array
1678      * @throws SerializationException on unknown data type
1679      */
1680     public static byte[] encodeUTF16(final EndianUtil endianUtil, final Object content) throws SerializationException
1681     {
1682         return encode(false, endianUtil, content);
1683     }
1684 
1685     /**
1686      * Find the serializer for one object.
1687      * @param utf8 if true; use UTF8 encoding for characters and Strings; if false; use UTF16 encoding for characters and
1688      *            Strings
1689      * @param object the object for which the serializer must be returned
1690      * @return the serializer needed for <code>object</code>
1691      * @throws SerializationException when there is no known serializer for <code>object</code>
1692      */
1693     @SuppressWarnings("checkstyle:needbraces")
1694     protected static Serializer<?> findEncoder(final boolean utf8, final Object object) throws SerializationException
1695     {
1696         Serializer<?> serializer = ENCODERS.get(object.getClass());
1697         if (serializer != null)
1698             return serializer;
1699         if (object instanceof Character)
1700             return utf8 ? CONVERT_CHARACTER8 : CONVERT_CHARACTER16;
1701         if (object instanceof String)
1702             return utf8 ? CONVERT_STRING8 : CONVERT_STRING16;
1703         if (object instanceof String[])
1704             return utf8 ? CONVERT_STRING8_ARRAY : CONVERT_STRING16_ARRAY;
1705         if (object instanceof String[][])
1706             return utf8 ? CONVERT_STRING8_MATRIX : CONVERT_STRING16_MATRIX;
1707         if (object instanceof FloatScalar)
1708             return CONVERT_DJUNITS_FLOAT_SCALAR;
1709         if (object instanceof DoubleScalar)
1710             return CONVERT_DJUNITS_DOUBLE_SCALAR;
1711         if (object instanceof FloatVector)
1712             return CONVERT_DJUNITS_FLOAT_VECTOR;
1713         if (object instanceof DoubleVector)
1714             return CONVERT_DJUNITS_DOUBLE_VECTOR;
1715         if (object instanceof FloatMatrix)
1716             return CONVERT_DJUNITS_FLOAT_MATRIX;
1717         if (object instanceof DoubleMatrix)
1718             return CONVERT_DJUNITS_DOUBLE_MATRIX;
1719         if (object instanceof SerializableObject[])
1720             return utf8 ? COMPOUND_ARRAY_SERIALIZER_UTF8 : COMPOUND_ARRAY_SERIALIZER_UTF16;
1721         if (object instanceof DoubleVector[])
1722             return CONVERT_DOUBLE_UNIT_COLUMN_VECTOR_ARRAY;
1723         if (object instanceof FloatVector[])
1724             return CONVERT_FLOAT_UNIT_COLUMN_VECTOR_ARRAY;
1725         else
1726             throw new SerializationException("Unhandled data type " + object.getClass());
1727     }
1728 
1729     /**
1730      * Encode the object array into a byte array, conforming to the provided endianness and string encodin.
1731      * @param utf8 whether to encode String fields and characters in utf8 or not
1732      * @param endianUtil encoder for multi-byte values
1733      * @param content the objects to encode
1734      * @return the zeroMQ message to send as a byte array
1735      * @throws SerializationException on unknown data type
1736      */
1737     @SuppressWarnings({"unchecked", "rawtypes"})
1738     private static byte[] encode(final boolean utf8, final EndianUtil endianUtil, final Object content)
1739             throws SerializationException
1740     {
1741         Serializer serializer = findEncoder(utf8, content);
1742         // Pass one: compute total size
1743         int size = serializer.sizeWithPrefix(content);
1744         // Allocate buffer
1745         byte[] buffer = new byte[size];
1746         // Pass 2 fill buffer
1747         Pointer pointer = new Pointer();
1748 
1749         serializer.serializeWithPrefix(content, buffer, pointer, endianUtil);
1750         Throw.when(pointer.get() != buffer.length, SerializationException.class, "Data size error (reserved %d, used %d)",
1751                 buffer.length, pointer.get());
1752         return buffer;
1753     }
1754 
1755     /**
1756      * Decode the message into an object, constructing Java primitives, primitive data arrays and matrices where possible.
1757      * @param buffer the byte array to decode
1758      * @return an object of the right type
1759      * @throws SerializationException on unknown data type
1760      */
1761     public static Object decodeToPrimitiveDataTypes(final byte[] buffer) throws SerializationException
1762     {
1763         return decode(buffer, PRIMITIVE_DATA_DECODERS);
1764     }
1765 
1766     /**
1767      * Decode the message into an object, constructing Java Objects, Object arrays and matrices where possible.
1768      * @param buffer the byte array to decode
1769      * @return an object of the right type
1770      * @throws SerializationException on unknown data type
1771      */
1772     public static Object decodeToObjectDataTypes(final byte[] buffer) throws SerializationException
1773     {
1774         return decode(buffer, OBJECT_DECODERS);
1775     }
1776 
1777     /**
1778      * Decode the message into a single object.
1779      * @param buffer the byte array to decode
1780      * @param decoderMap the map with decoders to use
1781      * @return an object of the right type
1782      * @throws SerializationException on unknown data type
1783      */
1784     public static Object decode(final byte[] buffer, final Map<Byte, Serializer<?>> decoderMap) throws SerializationException
1785     {
1786         Pointer pointer = new Pointer();
1787         EndianUtil endianUtil = EndianUtil.BIG_ENDIAN;
1788         Byte fieldType = buffer[pointer.getAndIncrement(1)];
1789         if (fieldType < 0)
1790         {
1791             fieldType = (byte) (fieldType & 0x7F);
1792             endianUtil = EndianUtil.LITTLE_ENDIAN;
1793         }
1794         Serializer<?> serializer = decoderMap.get(fieldType);
1795         if (null == serializer)
1796         {
1797             throw new SerializationException(
1798                     "Bad FieldType or no defined decoder for fieldType " + fieldType + " at position " + (pointer.get() - 1));
1799         }
1800         else
1801         {
1802             return serializer.deSerialize(buffer, pointer, endianUtil);
1803         }
1804     }
1805 
1806     /**
1807      * Decode a byte value from the buffer, including the prefix.
1808      * @param buffer the buffer with the byte-encoded byte
1809      * @return the byte value belonging to the byte buffer content
1810      * @throws SerializationException when decoding fails
1811      */
1812     public static byte decodeByte(final byte[] buffer) throws SerializationException
1813     {
1814         Throw.when(buffer.length < 2, SerializationException.class, "decodeByte expects a buffer of at least 2 bytes");
1815         if (buffer[0] == FieldTypes.BYTE_8 || buffer[0] == FieldTypes.BYTE_8_LE)
1816         {
1817             return (byte) decodeToPrimitiveDataTypes(buffer);
1818         }
1819         throw new SerializationException("decodeByte did not detect byte in first byte");
1820     }
1821 
1822     /**
1823      * Decode a short value from the buffer, including the prefix.
1824      * @param buffer the buffer with the byte-encoded short
1825      * @return the short value belonging to the byte buffer content
1826      * @throws SerializationException when decoding fails
1827      */
1828     public static short decodeShort(final byte[] buffer) throws SerializationException
1829     {
1830         Throw.when(buffer.length < 3, SerializationException.class, "decodeShort expects a buffer of at least 3 bytes");
1831         if (buffer[0] == FieldTypes.SHORT_16 || buffer[0] == FieldTypes.SHORT_16_LE)
1832         {
1833             return (short) decodeToPrimitiveDataTypes(buffer);
1834         }
1835         throw new SerializationException("decodeShort did not detect short in first byte");
1836     }
1837 
1838     /**
1839      * Decode an integer value from the buffer, including the prefix.
1840      * @param buffer the buffer with the byte-encoded int
1841      * @return the integer value belonging to the byte buffer content
1842      * @throws SerializationException when decoding fails
1843      */
1844     public static int decodeInt(final byte[] buffer) throws SerializationException
1845     {
1846         Throw.when(buffer.length < 5, SerializationException.class, "decodeInt expects a buffer of at least 5 bytes");
1847         if (buffer[0] == FieldTypes.INT_32 || buffer[0] == FieldTypes.INT_32_LE)
1848         {
1849             return (int) decodeToPrimitiveDataTypes(buffer);
1850         }
1851         throw new SerializationException("decodeInt did not detect integer in first byte");
1852     }
1853 
1854     /**
1855      * Decode a long value from the buffer, including the prefix.
1856      * @param buffer the buffer with the byte-encoded long
1857      * @return the long value belonging to the byte buffer content
1858      * @throws SerializationException when decoding fails
1859      */
1860     public static long decodeLong(final byte[] buffer) throws SerializationException
1861     {
1862         Throw.when(buffer.length < 9, SerializationException.class, "decodeLong expects a buffer of at least 9 bytes");
1863         if (buffer[0] == FieldTypes.LONG_64 || buffer[0] == FieldTypes.LONG_64_LE)
1864         {
1865             return (long) decodeToPrimitiveDataTypes(buffer);
1866         }
1867         throw new SerializationException("decodeLong did not detect long in first byte");
1868     }
1869 
1870     /**
1871      * Decode a float value from the buffer, including the prefix.
1872      * @param buffer the buffer with the byte-encoded float
1873      * @return the float value belonging to the byte buffer content
1874      * @throws SerializationException when decoding fails
1875      */
1876     public static float decodeFloat(final byte[] buffer) throws SerializationException
1877     {
1878         Throw.when(buffer.length < 5, SerializationException.class, "decodeFloat expects a buffer of at least 5 bytes");
1879         if (buffer[0] == FieldTypes.FLOAT_32 || buffer[0] == FieldTypes.FLOAT_32_LE)
1880         {
1881             return (float) decodeToPrimitiveDataTypes(buffer);
1882         }
1883         throw new SerializationException("decodeFloat did not detect float in first byte");
1884     }
1885 
1886     /**
1887      * Decode a double value from the buffer, including the prefix.
1888      * @param buffer the buffer with the byte-encoded double
1889      * @return the double value belonging to the byte buffer content
1890      * @throws SerializationException when decoding fails
1891      */
1892     public static double decodeDouble(final byte[] buffer) throws SerializationException
1893     {
1894         Throw.when(buffer.length < 9, SerializationException.class, "decodeDouble expects a buffer of at least 9 bytes");
1895         if (buffer[0] == FieldTypes.DOUBLE_64 || buffer[0] == FieldTypes.DOUBLE_64_LE)
1896         {
1897             return (double) decodeToPrimitiveDataTypes(buffer);
1898         }
1899         throw new SerializationException("decodeDouble did not detect double in first byte");
1900     }
1901 
1902     /**
1903      * Decode a boolean value from the buffer, including the prefix.
1904      * @param buffer the buffer with the byte-encoded boolean
1905      * @return the boolean value belonging to the byte buffer content
1906      * @throws SerializationException when decoding fails
1907      */
1908     public static boolean decodeBoolean(final byte[] buffer) throws SerializationException
1909     {
1910         Throw.when(buffer.length < 2, SerializationException.class, "decodeBoolean expects a buffer of at least 2 bytes");
1911         if (buffer[0] == FieldTypes.BOOLEAN_8 || buffer[0] == FieldTypes.BOOLEAN_8_LE)
1912         {
1913             return (boolean) decodeToPrimitiveDataTypes(buffer);
1914         }
1915         throw new SerializationException("decodeBoolean did not detect boolean in first byte");
1916     }
1917 
1918     /**
1919      * Decode a char value from the buffer, including the prefix, based on a one-byte UTF-8 value.
1920      * @param buffer the buffer with the byte-encoded char
1921      * @return the char value belonging to the byte buffer content
1922      * @throws SerializationException when decoding fails
1923      */
1924     public static char decodeCharUtf8(final byte[] buffer) throws SerializationException
1925     {
1926         Throw.when(buffer.length < 2, SerializationException.class, "decodeShort expects a buffer of at least 2 bytes");
1927         if (buffer[0] == FieldTypes.CHAR_8 || buffer[0] == FieldTypes.CHAR_8_LE)
1928         {
1929             return (char) decodeToPrimitiveDataTypes(buffer);
1930         }
1931         throw new SerializationException("decodeCharUtf8 did not detect char in first byte");
1932     }
1933 
1934     /**
1935      * Decode a char value from the buffer, including the prefix, based on a one-byte UTF-16 value.
1936      * @param buffer the buffer with the byte-encoded char
1937      * @return the char value belonging to the byte buffer content
1938      * @throws SerializationException when decoding fails
1939      */
1940     public static char decodeCharUtf16(final byte[] buffer) throws SerializationException
1941     {
1942         Throw.when(buffer.length < 3, SerializationException.class, "decodeShort expects a buffer of at least 3 bytes");
1943         if (buffer[0] == FieldTypes.CHAR_16 || buffer[0] == FieldTypes.CHAR_16_LE)
1944         {
1945             return (char) decodeToPrimitiveDataTypes(buffer);
1946         }
1947         throw new SerializationException("decodeCharUtf8 did not detect char in first byte");
1948     }
1949 
1950     /**
1951      * Retrieve and decode a DJUNITS unit.
1952      * @param buffer the encoded data
1953      * @param pointer position in the encoded data where the unit is to be decoded from
1954      * @param endianUtil decoder for multi-byte values
1955      * @return decoded Unit
1956      * @param <U> the unit type
1957      */
1958     @SuppressWarnings("unchecked")
1959     public static <U extends Unit<U>> U getUnit(final byte[] buffer, final Pointer pointer, final EndianUtil endianUtil)
1960     {
1961         SerializationUnits unitType = SerializationUnits.getUnitType(buffer[pointer.getAndIncrement(1)]);
1962         DisplayType displayType = DisplayType.getDisplayType(unitType, 0 + buffer[pointer.getAndIncrement(1)]);
1963         return (U) displayType.getDjunitsType();
1964     }
1965 
1966     /**
1967      * Minimal implementation of SerializableObject.
1968      */
1969     static class MinimalSerializableObject implements SerializableObject<MinimalSerializableObject>
1970     {
1971         /** The List that is returned by the <code>exportAsList</code> method. */
1972         private final List<Object> list;
1973 
1974         /**
1975          * Construct a new MinimalCompound object.
1976          * @param list the object list that is returned by <code>exportAsList</code> method
1977          */
1978         MinimalSerializableObject(final List<Object> list)
1979         {
1980             this.list = list;
1981         }
1982 
1983         @Override
1984         public List<Object> exportAsList()
1985         {
1986             return this.list;
1987         }
1988     }
1989 
1990 }