1 /*
2  * hunt-proton: AMQP Protocol library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.proton.codec.ListType;
13 
14 import hunt.proton.codec.DecoderImpl;
15 import hunt.proton.codec.EncoderImpl;
16 import hunt.proton.codec.PrimitiveTypeEncoding;
17 import hunt.proton.codec.AbstractPrimitiveType;
18 import hunt.proton.codec.LargeFloatingSizePrimitiveTypeEncoding;
19 import hunt.proton.codec.AMQPType;
20 import hunt.proton.codec.TypeEncoding;
21 import hunt.proton.codec.TypeConstructor;
22 import hunt.proton.codec.EncodingCodes;
23 import hunt.proton.codec.ReadableBuffer;
24 import hunt.proton.codec.SmallFloatingSizePrimitiveTypeEncoding;
25 import hunt.proton.codec.FixedSizePrimitiveTypeEncoding;
26 import hunt.proton.codec.NullType;
27 import hunt.proton.codec.BooleanType;
28 import hunt.proton.codec.ByteType;
29 import hunt.proton.codec.ShortType;
30 import hunt.proton.codec.IntegerType;
31 import hunt.proton.codec.LongType;
32 import hunt.proton.codec.FloatType;
33 import hunt.proton.codec.DoubleType;
34 import hunt.proton.codec.CharacterType;
35 
36 import hunt.Boolean;
37 import hunt.collection.Collection;
38 import hunt.collection.Collections;
39 import hunt.collection.List;
40 import hunt.collection.ArrayList;
41 import hunt.Exceptions;
42 import hunt.Short;
43 import hunt.Integer;
44 import hunt.Long;
45 import hunt.Float;
46 import hunt.Double;
47 import hunt.logging.ConsoleLogger;
48 
49 import std.conv : to;
50 
51 interface ListEncoding : PrimitiveTypeEncoding!(List!Object) {
52     void setValue(List!Object value, int length);
53     Object readValueArray();
54 }
55 
56 class ListType : AbstractPrimitiveType!(List!Object) {
57     private ListEncoding _listEncoding;
58     private ListEncoding _shortListEncoding;
59     private ListEncoding _zeroListEncoding;
60     private EncoderImpl _encoder;
61 
62     this(EncoderImpl encoder, DecoderImpl decoder) {
63         _encoder = encoder;
64         _listEncoding = new AllListEncoding(encoder, decoder);
65         _shortListEncoding = new ShortListEncoding(encoder, decoder);
66         _zeroListEncoding = new ZeroListEncoding(encoder, decoder);
67         encoder.register(typeid(List!Object), this);
68         decoder.register(this);
69     }
70 
71     public TypeInfo getTypeClass() {
72         return typeid(List!Object);
73     }
74 
75     public ITypeEncoding getEncoding(Object v) {
76         List!Object val = cast(List!Object) v;
77         int calculatedSize = calculateSize(val, _encoder);
78         ListEncoding encoding = val.isEmpty() ? _zeroListEncoding : (val.size() > 255
79                 || calculatedSize >= 254) ? _listEncoding : _shortListEncoding;
80 
81         encoding.setValue(val, calculatedSize);
82         return encoding;
83     }
84 
85     // public static TypeInfo
86     private static List!Object decodeBooleanArray(BooleanEncoding constructor, int count) {
87         // boolean[] array = new boolean[count];
88         List!Object array = new ArrayList!Object;
89         for (int i = 0; i < count; i++) {
90             array.add(new Boolean(constructor.readPrimitiveValue()));
91         }
92 
93         return array;
94     }
95 
96     private static List!Object decodeByteArray(ByteType.ByteEncoding constructor, int count) {
97         List!Object array = new ArrayList!Object;
98 
99         for (int i = 0; i < count; i++) {
100             array.add(constructor.readPrimitiveValue());
101         }
102 
103         return array;
104     }
105 
106     private static List!Object decodeShortArray(ShortType.ShortEncoding constructor, int count) {
107         List!Object array = new ArrayList!Object;
108 
109         for (int i = 0; i < count; i++) {
110             array.add(new Short(constructor.readPrimitiveValue()));
111         }
112 
113         return array;
114     }
115 
116     private static List!Object decodeIntArray(IntegerType.IntegerEncoding constructor, int count) {
117         List!Object array = new ArrayList!Object;
118 
119         for (int i = 0; i < count; i++) {
120             array.add(new Integer(constructor.readPrimitiveValue()));
121         }
122 
123         return array;
124     }
125 
126     private static List!Object decodeLongArray(LongType.LongEncoding constructor, int count) {
127         List!Object array = new ArrayList!Object;
128 
129         for (int i = 0; i < count; i++) {
130             array.add(new Long(constructor.readPrimitiveValue()));
131         }
132 
133         return array;
134     }
135 
136     private static List!Object decodeFloatArray(FloatType.FloatEncoding constructor, int count) {
137         List!Object array = new ArrayList!Object;
138         for (int i = 0; i < count; i++) {
139             array.add(new Float(constructor.readPrimitiveValue()));
140         }
141 
142         return array;
143     }
144 
145     private static List!Object decodeDoubleArray(DoubleType.DoubleEncoding constructor, int count) {
146         List!Object array = new ArrayList!Object;
147 
148         for (int i = 0; i < count; i++) {
149             array.add(new Double(constructor.readPrimitiveValue()));
150         }
151 
152         return array;
153     }
154 
155     private static List!Object decodeCharArray(CharacterType.CharacterEncoding constructor,
156             int count) {
157         List!Object array = new ArrayList!Object;
158 
159         for (int i = 0; i < count; i++) {
160             array.add(constructor.readPrimitiveValue());
161         }
162 
163         return array;
164     }
165 
166     private static Object decodeArrayAsObject(DecoderImpl decoder, int count) {
167         ITypeConstructor constructor = decoder.readConstructor(true);
168         if (constructor.encodesJavaPrimitive()) {
169             if (count > decoder.getByteBufferRemaining()) {
170                 throw new IllegalArgumentException("Array element count error");
171             }
172 
173             if (cast(BooleanEncoding) constructor !is null) {
174                 return cast(Object)(decodeBooleanArray(cast(BooleanEncoding) constructor, count));
175             } else if (cast(ByteType.ByteEncoding) constructor !is null) {
176                 return cast(Object)(decodeByteArray(cast(ByteType.ByteEncoding) constructor, count));
177             } else if (cast(ShortType.ShortEncoding) constructor !is null) {
178                 return cast(Object)(decodeShortArray(cast(ShortType.ShortEncoding) constructor,
179                         count));
180             } else if (cast(IntegerType.IntegerEncoding) constructor !is null) {
181                 return cast(Object)(decodeIntArray(cast(IntegerType.IntegerEncoding) constructor,
182                         count));
183             } else if (cast(LongType.LongEncoding) constructor !is null) {
184                 return cast(Object)(decodeLongArray(cast(LongType.LongEncoding) constructor, count));
185             } else if (cast(FloatType.FloatEncoding) constructor !is null) {
186                 return cast(Object)(decodeFloatArray(cast(FloatType.FloatEncoding) constructor,
187                         count));
188             } else if (cast(DoubleType.DoubleEncoding) constructor !is null) {
189                 return cast(Object)(decodeDoubleArray(cast(DoubleType.DoubleEncoding) constructor,
190                         count));
191             } else if (cast(CharacterType.CharacterEncoding) constructor !is null) {
192                 return cast(Object)(decodeCharArray(cast(CharacterType.CharacterEncoding) constructor,
193                         count));
194             } else {
195                 throw new ClassCastException("Unexpected class ");
196             }
197         } else {
198             return cast(Object)(decodeNonPrimitive(decoder, constructor, count));
199         }
200     }
201 
202     private static List!Object decodeNonPrimitive(DecoderImpl decoder,
203             ITypeConstructor constructor, int count) {
204         if (count > decoder.getByteBufferRemaining()) {
205             throw new IllegalArgumentException("Array element count " ~ to!string(
206                     count) ~ " is specified to be greater than the amount of data available (" ~ to!string(
207                     decoder.getByteBufferRemaining()) ~ ")");
208         }
209 
210         if (cast(ShortListEncoding) constructor !is null) {
211             ShortListEncoding arrayEncoding = cast(ShortListEncoding) constructor;
212 
213             // Object[] array = new Object[count];
214             List!Object ob = new ArrayList!Object;
215             for (int i = 0; i < count; i++) {
216                 ob.add(arrayEncoding.readValueArray());
217             }
218 
219             return ob;
220         } else if (cast(AllListEncoding) constructor !is null) {
221             AllListEncoding arrayEncoding = cast(AllListEncoding) constructor;
222 
223             // Object[] array = new Object[count];
224             List!Object ob = new ArrayList!Object;
225             for (int i = 0; i < count; i++) {
226                 ob.add(arrayEncoding.readValueArray());
227             }
228 
229             return ob;
230         } else {
231             // Object[] array = (Object[]) Array.newInstance(constructor.getTypeClass(), count);
232 
233             List!Object array = new ArrayList!Object;
234             for (int i = 0; i < count; i++) {
235                 array.add(constructor.readValue());
236             }
237 
238             return array;
239         }
240     }
241 
242     private static int calculateSize(List!Object val, EncoderImpl encoder) {
243         int len = 0;
244         int count = val.size();
245         for (int i = 0; i < count; i++) {
246             Object element = val.get(i);
247             //logInfo("i : %d" , i );
248             //if (element !is null)
249             //{
250             //    logInfo(" %s" ,  typeid(element).toString );
251             //}
252             IAMQPType type = encoder.getType(element);
253             //if (cast( NullType)type !is null)
254             //{
255             //    logInfo("null!!!!!!!!!!!!!!!!!!!!!!");
256             //}
257             if (type is null) {
258                 logError("!!!!!!logError!!!!!!!!!!!!!!!!");
259                 //  throw new IllegalArgumentException("No encoding defined for type: ");
260             }
261             ITypeEncoding elementEncoding = type.getEncoding(element);
262 
263             //NullEncoding n = cast(NullEncoding)elementEncoding;
264             //if (n !is null)
265             //{
266             //    logInfo("n!!!!!!!!!!!!!!!!!!!!!!");
267             //}
268 
269             len += elementEncoding.getConstructorSize() + elementEncoding.getValueSize(element);
270         }
271         return len;
272     }
273 
274     public ListEncoding getCanonicalEncoding() {
275         return _listEncoding;
276     }
277 
278     public Collection!(TypeEncoding!(List!Object)) getAllEncodings() {
279         List!(TypeEncoding!(List!Object)) lst = new ArrayList!(TypeEncoding!(List!Object));
280         lst.add(_zeroListEncoding);
281         lst.add(_shortListEncoding);
282         lst.add(_listEncoding);
283         return lst;
284     }
285 
286     class AllListEncoding : LargeFloatingSizePrimitiveTypeEncoding!(List!Object), ListEncoding {
287 
288         private List!Object _value;
289         private int _length;
290 
291         this(EncoderImpl encoder, DecoderImpl decoder) {
292             super(encoder, decoder);
293         }
294 
295         override protected void writeEncodedValue(List!Object val) {
296             getEncoder().getBuffer().ensureRemaining(getSizeBytes() + getEncodedValueSize(val));
297             getEncoder().writeRaw(val.size());
298 
299             int count = val.size();
300 
301             for (int i = 0; i < count; i++) {
302                 Object element = val.get(i);
303                 ITypeEncoding elementEncoding = getEncoder().getType(element).getEncoding(element);
304                 elementEncoding.writeConstructor();
305                 elementEncoding.writeValue(element);
306             }
307         }
308 
309         override protected int getEncodedValueSize(List!Object val) {
310             return 4 + ((val == _value) ? _length : calculateSize(val, getEncoder()));
311         }
312 
313         override public byte getEncodingCode() {
314             return EncodingCodes.LIST32;
315         }
316 
317         public Object readValueArray() {
318             DecoderImpl decoder = getDecoder();
319             int size = (cast(int) decoder.readRawByte()) & 0xFF;
320             int count = (cast(int) decoder.readRawByte()) & 0xFF;
321             return decodeArrayAsObject(decoder, count);
322         }
323 
324         override public ListType getType() {
325             return this.outer;
326         }
327 
328         override public bool encodesSuperset(TypeEncoding!(List!Object) encoding) {
329             return (getType() == encoding.getType());
330         }
331 
332         override public Object readValue() {
333             DecoderImpl decoder = getDecoder();
334             ReadableBuffer buffer = decoder.getBuffer();
335 
336             int size = decoder.readRawInt();
337             // todo - limit the decoder with size
338             int count = decoder.readRawInt();
339             // Ensure we do not allocate an array of size greater then the available data, otherwise there is a risk for an OOM error
340             if (count > decoder.getByteBufferRemaining()) {
341                 throw new IllegalArgumentException("List element count " ~ to!string(
342                         count) ~ " is specified to be greater than the amount of data available (" ~ to!string(
343                         decoder.getByteBufferRemaining()) ~ ")");
344             }
345 
346             ITypeConstructor typeConstructor = null;
347 
348             List!(Object) list = new ArrayList!(Object)(count);
349             for (int i = 0; i < count; i++) {
350                 bool arrayType = false;
351                 byte encodingCode = buffer.get(buffer.position());
352                 switch (encodingCode) {
353                 case EncodingCodes.ARRAY8:
354                     goto case;
355                 case EncodingCodes.ARRAY32:
356                     arrayType = true;
357                     break;
358                 default:
359                     break;
360                 }
361 
362                 // Whenever we can just reuse the previously used TypeDecoder instead
363                 // of spending time looking up the same one again.
364                 if (typeConstructor is null) {
365                     typeConstructor = getDecoder().readConstructor();
366                 } else {
367                     if (encodingCode == EncodingCodes.DESCRIBED_TYPE_INDICATOR) {
368                         typeConstructor = getDecoder().readConstructor();
369                     } else {
370                         IPrimitiveTypeEncoding primitiveConstructor = cast(IPrimitiveTypeEncoding) typeConstructor;
371                         if (encodingCode != primitiveConstructor.getEncodingCode()) {
372                             typeConstructor = getDecoder().readConstructor();
373                         } else {
374                             // consume the encoding code byte for real
375                             encodingCode = buffer.get();
376                         }
377                     }
378                 }
379 
380                 if (typeConstructor is null) {
381                     // throw new DecodeException("Unknown constructor");
382                     logError("Unknown constructor");
383                 }
384 
385                 version (HUNT_AMQP_DEBUG) tracef("Constructor: %s", typeid(typeConstructor));
386 
387                 Object value;
388 
389                 if (arrayType) {
390                     value = (cast(ListEncoding) typeConstructor).readValueArray();
391                 } else {
392                     value = typeConstructor.readValue();
393                 }
394                 list.add(value);
395             }
396 
397             return cast(Object) list;
398         }
399 
400         override public void skipValue() {
401             DecoderImpl decoder = getDecoder();
402             ReadableBuffer buffer = decoder.getBuffer();
403             int size = decoder.readRawInt();
404             buffer.position(buffer.position() + size);
405         }
406 
407         override public void setValue(List!Object value, int length) {
408             _value = value;
409             _length = length;
410         }
411 
412         override void writeConstructor() {
413             super.writeConstructor();
414         }
415 
416         override int getConstructorSize() {
417             return super.getConstructorSize();
418         }
419 
420     }
421 
422     class ShortListEncoding : SmallFloatingSizePrimitiveTypeEncoding!(List!Object), ListEncoding {
423 
424         private List!Object _value;
425         private int _length;
426 
427         this(EncoderImpl encoder, DecoderImpl decoder) {
428             super(encoder, decoder);
429         }
430 
431         override protected void writeEncodedValue(List!Object val) {
432             getEncoder().getBuffer().ensureRemaining(getSizeBytes() + getEncodedValueSize(val));
433             getEncoder().writeRaw(cast(byte) val.size());
434 
435             int count = val.size();
436 
437             for (int i = 0; i < count; i++) {
438                 Object element = val.get(i);
439                 ITypeEncoding elementEncoding = getEncoder().getType(element).getEncoding(element);
440                 elementEncoding.writeConstructor();
441                 elementEncoding.writeValue(element);
442             }
443         }
444 
445         override protected int getEncodedValueSize(List!Object val) {
446             return 1 + ((val == _value) ? _length : calculateSize(val, getEncoder()));
447         }
448 
449         override public byte getEncodingCode() {
450             return EncodingCodes.LIST8;
451         }
452 
453         override public ListType getType() {
454             return this.outer;
455         }
456 
457         override public bool encodesSuperset(TypeEncoding!(List!Object) encoder) {
458             return encoder == this;
459         }
460 
461         public Object readValueArray() {
462             DecoderImpl decoder = getDecoder();
463             int size = (cast(int) decoder.readRawByte()) & 0xFF;
464             int count = (cast(int) decoder.readRawByte()) & 0xFF;
465             return decodeArrayAsObject(decoder, count);
466         }
467 
468         override public Object readValue() {
469             DecoderImpl decoder = getDecoder();
470             ReadableBuffer buffer = decoder.getBuffer();
471 
472             int size = (cast(int) decoder.readRawByte()) & 0xff;
473             // todo - limit the decoder with size
474             int count = (cast(int) decoder.readRawByte()) & 0xff;
475 
476             ITypeConstructor typeConstructor = null;
477 
478             List!(Object) list = new ArrayList!Object(count);
479             for (int i = 0; i < count; i++) {
480                 bool arrayType = false;
481                 byte encodingCode = buffer.get(buffer.position());
482                 switch (encodingCode) {
483                 case EncodingCodes.ARRAY8:
484                     goto case;
485                 case EncodingCodes.ARRAY32:
486                     arrayType = true;
487                     break;
488                 default:
489                     break;
490                 }
491 
492                 // Whenever we can just reuse the previously used TypeDecoder instead
493                 // of spending time looking up the same one again.
494                 if (typeConstructor is null) {
495                     typeConstructor = getDecoder().readConstructor();
496                 } else {
497                     //|| !(typeConstructor instanceof PrimitiveTypeEncoding<?>))
498                     if (encodingCode == EncodingCodes.DESCRIBED_TYPE_INDICATOR
499                             || (cast(IPrimitiveTypeEncoding) typeConstructor is null)) {
500                         typeConstructor = getDecoder().readConstructor();
501                     } else {
502                         IPrimitiveTypeEncoding primitiveConstructor = cast(IPrimitiveTypeEncoding) typeConstructor;
503                         if (encodingCode != primitiveConstructor.getEncodingCode()) {
504                             typeConstructor = getDecoder().readConstructor();
505                         } else {
506                             // consume the encoding code byte for real
507                             encodingCode = buffer.get();
508                         }
509                     }
510                 }
511 
512                 if (typeConstructor is null) {
513                     logError("Unknown constructor");
514                     //  throw new DecodeException("Unknown constructor");
515                 }
516 
517                 Object value;
518 
519                 if (arrayType) {
520                     value = (cast(ListEncoding) typeConstructor).readValueArray();
521                 } else {
522                     value = typeConstructor.readValue();
523                 }
524 
525                 list.add(value);
526             }
527 
528             return cast(Object) list;
529         }
530 
531         override public void skipValue() {
532             DecoderImpl decoder = getDecoder();
533             ReadableBuffer buffer = decoder.getBuffer();
534             int size = (cast(int) decoder.readRawByte()) & 0xff;
535             buffer.position(buffer.position() + size);
536         }
537 
538         override public void setValue(List!Object value, int length) {
539             _value = value;
540             _length = length;
541         }
542 
543         override void writeConstructor() {
544             super.writeConstructor();
545         }
546 
547         override int getConstructorSize() {
548             return super.getConstructorSize();
549         }
550 
551     }
552 
553     class ZeroListEncoding : FixedSizePrimitiveTypeEncoding!(List!Object), ListEncoding {
554         this(EncoderImpl encoder, DecoderImpl decoder) {
555             super(encoder, decoder);
556         }
557 
558         override public byte getEncodingCode() {
559             return EncodingCodes.LIST0;
560         }
561 
562         override protected int getFixedSize() {
563             return 0;
564         }
565 
566         override public ListType getType() {
567             return this.outer;
568         }
569 
570         Object readValueArray() {
571             return null;
572         }
573 
574         override public void setValue(List!Object value, int length) {
575         }
576 
577         override public void writeValue(Object val) {
578         }
579 
580         override public bool encodesSuperset(TypeEncoding!(List!Object) encoder) {
581             return encoder == this;
582         }
583 
584         override public Object readValue() {
585             return new ArrayList!Object;
586         }
587 
588         override void writeConstructor() {
589             super.writeConstructor();
590         }
591 
592         override int getConstructorSize() {
593             return super.getConstructorSize();
594         }
595 
596     }
597 }