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.MapType;
13 
14 import hunt.proton.codec.PrimitiveTypeEncoding;
15 import hunt.proton.codec.AMQPType;
16 import hunt.proton.codec.EncoderImpl;
17 import hunt.proton.codec.DecoderImpl;
18 import hunt.proton.codec.AbstractPrimitiveType;
19 import hunt.collection.Collection;
20 import hunt.collection.Map;
21 import hunt.collection.ArrayList;
22 import hunt.proton.codec.LargeFloatingSizePrimitiveTypeEncoding;
23 import hunt.Exceptions;
24 import hunt.Object;
25 import hunt.collection.Map;
26 import hunt.String;
27 import hunt.proton.codec.TypeEncoding;
28 import hunt.proton.codec.EncodingCodes;
29 import hunt.proton.codec.StringType;
30 import hunt.collection.LinkedHashMap;
31 import hunt.proton.codec.TypeConstructor;
32 import hunt.proton.codec.ReadableBuffer;
33 import hunt.proton.codec.SmallFloatingSizePrimitiveTypeEncoding;
34 import hunt.logging;
35 import std.conv : to;
36 
37 interface MapEncoding : PrimitiveTypeEncoding!(Map!(String,Object))
38 {
39    void setValue(Map!(String,Object) value, int length);
40 }
41 
42 class MapType : AbstractPrimitiveType!(Map!(String,Object))
43 {
44     private MapEncoding _mapEncoding;
45     private MapEncoding _shortMapEncoding;
46     private EncoderImpl _encoder;
47 
48     //private AMQPType<?> fixedKeyType;
49 
50     private IAMQPType  fixedKeyType;
51 
52 
53     this(EncoderImpl encoder, DecoderImpl decoder)
54     {
55         _encoder = encoder;
56         _mapEncoding = new AllMapEncoding(encoder, decoder);
57         _shortMapEncoding = new ShortMapEncoding(encoder, decoder);
58         encoder.register(typeid(LinkedHashMap!(String,Object)), this);
59         decoder.register(this);
60     }
61 
62     public TypeInfo getTypeClass()
63     {
64         return typeid(LinkedHashMap!(String,Object));
65     }
66 
67     public void setKeyEncoding(IAMQPType keyType)
68     {
69         this.fixedKeyType = keyType;
70     }
71 
72     public ITypeEncoding getEncoding(Object val)
73     {
74         int calculatedSize = calculateSize(cast(Map!(String,Object))val);
75         MapEncoding encoding = ((cast(Map!(String,Object))val).size() > 127 || calculatedSize >= 254)
76                                     ? _mapEncoding
77                                     : _shortMapEncoding;
78 
79         encoding.setValue(cast(Map!(String,Object))val, calculatedSize);
80         return encoding;
81     }
82 
83     private int calculateSize(Map!(String,Object) map)
84     {
85 
86         //implementationMissing(false);
87         int len = 0;
88 
89         //Iterator<Map.Entry<?, ?>> iter = map.entrySet().iterator();
90         IAMQPType IfixedKeyType = this.fixedKeyType;
91 
92         // Clear existing fixed key type encoding to prevent application to nested Maps
93         setKeyEncoding(null);
94 
95         try
96         {
97 
98             foreach (MapEntry!(String, Object) element ; map)
99             {
100                 ITypeEncoding elementEncoding;
101                 if (IfixedKeyType is null)
102                 {
103                     IAMQPType tmp = _encoder.getType( element.getKey());
104                     if (tmp is null)
105                     {
106                         logError( "getType Error");
107                     }
108 
109                     elementEncoding = tmp.getEncoding( element.getKey());
110                     //  elementEncoding = _encoder.getType(element.getKey()).getEncoding(element.getKey());
111                 }
112                 else
113                 {
114                     elementEncoding = IfixedKeyType.getEncoding( element.getKey());
115                 }
116 
117                 len += elementEncoding.getConstructorSize() + elementEncoding.getValueSize( element.getKey());
118 
119 
120 
121 
122                 IAMQPType tmp = (_encoder.getType( element.getValue()));
123                 if (tmp is null)
124                 {
125                     logError( "getType Error");
126                 }
127                 ITypeEncoding  elementEncodingVal = tmp.getEncoding( element.getValue());
128                 len += elementEncodingVal.getConstructorSize() + elementEncodingVal.getValueSize( element.getValue());
129 
130 
131                 //if (cast(String)element.getValue() !is null)
132                 //{
133                 //    StringType tmp = cast(StringType)(_encoder.getType(element.getValue()));
134                 //    if (tmp is null)
135                 //    {
136                 //        logError("getType Error");
137                 //    }
138                 //
139                 //    StringEncoding  elementEncodingVal = tmp.getEncoding(cast(String)element.getValue());
140                 //    len += elementEncodingVal.getConstructorSize() + elementEncodingVal.getValueSize(cast(String)element.getValue());
141                 //}else
142                 //{
143                 //    logError("unknown type");
144                 //}
145             }
146         } finally {
147             setKeyEncoding(IfixedKeyType);
148         }
149 
150 
151 
152         //try {
153         //    while (iter.hasNext())
154         //    {
155         //        Map.Entry<?, ?> element = iter.next();
156         //        TypeEncoding elementEncoding;
157         //
158         //        if (fixedKeyType is null)
159         //        {
160         //            elementEncoding = _encoder.getType(element.getKey()).getEncoding(element.getKey());
161         //        }
162         //        else
163         //        {
164         //            elementEncoding = fixedKeyType.getEncoding(element.getKey());
165         //        }
166         //
167         //        len += elementEncoding.getConstructorSize() + elementEncoding.getValueSize(element.getKey());
168         //        elementEncoding = _encoder.getType(element.getValue()).getEncoding(element.getValue());
169         //        len += elementEncoding.getConstructorSize() + elementEncoding.getValueSize(element.getValue());
170         //    }
171         //} finally {
172         //    // Reset Existing key type encoding for later encode step or reuse until cleared by caller
173         //    setKeyEncoding(fixedKeyType);
174         //}
175 
176         //scope(exit)
177         //{
178         //    setKeyEncoding(IfixedKeyType);
179         //}
180 
181         return len;
182     }
183 
184 
185     private static ITypeConstructor findNextDecoder(DecoderImpl decoder, ReadableBuffer buffer, ITypeConstructor previousConstructor)
186     {
187         if (previousConstructor is null)
188         {
189             return decoder.readConstructor();
190         }
191         else
192         {
193             byte encodingCode = buffer.get(buffer.position());
194             if (encodingCode == EncodingCodes.DESCRIBED_TYPE_INDICATOR)
195             {
196                 return decoder.readConstructor();
197             }
198             else
199             {
200                 IPrimitiveTypeEncoding primitiveConstructor = cast(IPrimitiveTypeEncoding) previousConstructor;
201                 if (encodingCode != primitiveConstructor.getEncodingCode())
202                 {
203                     return decoder.readConstructor();
204                 }
205                 else
206                 {
207                     // consume the encoding code byte for real
208                     encodingCode = buffer.get();
209                 }
210             }
211         }
212 
213         return previousConstructor;
214     }
215 
216     public MapEncoding getCanonicalEncoding()
217     {
218         return _mapEncoding;
219     }
220 
221 
222 
223     //Collection!(TypeEncoding!(Map!(String,Object)))
224     public Collection!(TypeEncoding!(Map!(String,Object))) getAllEncodings()
225     {
226         ArrayList!(TypeEncoding!(Map!(String,Object))) lst = new ArrayList!(TypeEncoding!(Map!(String,Object)))();
227         lst.add(_shortMapEncoding);
228         lst.add(_mapEncoding);
229         return lst;
230        // return Arrays.asList(_shortMapEncoding, _mapEncoding);
231     }
232 
233     class AllMapEncoding
234             : LargeFloatingSizePrimitiveTypeEncoding!(Map!(String,Object))
235             , MapEncoding
236     {
237 
238         private Map!(String,Object) _value;
239         private int _length;
240 
241         this(EncoderImpl encoder, DecoderImpl decoder)
242         {
243             super(encoder, decoder);
244         }
245 
246         override
247         protected void writeEncodedValue(Map!(String,Object) map)
248         {
249             getEncoder().getBuffer().ensureRemaining(getSizeBytes() + getEncodedValueSize(map));
250             getEncoder().writeRaw(2 * map.size());
251 
252           //  Iterator<Map.Entry> iter = map.entrySet().iterator();
253             IAMQPType IfixedKeyType =  this.outer.fixedKeyType;
254 
255             // Clear existing fixed key type encoding to prevent application to nested Maps
256             setKeyEncoding(null);
257 
258             try
259             {
260                 foreach (MapEntry!(String, Object) element ; map)
261                 {
262                     ITypeEncoding elementEncoding;
263                     if (IfixedKeyType is null)
264                     {
265                         StringType tmp = cast(StringType)(_encoder.getType( element.getKey()));
266                         if (tmp is null)
267                         {
268                             logError( "getType Error");
269                         }
270 
271                         elementEncoding = tmp.getEncoding( element.getKey());
272                         //  elementEncoding = _encoder.getType(element.getKey()).getEncoding(element.getKey());
273                     }
274                     else
275                     {
276                         elementEncoding = IfixedKeyType.getEncoding( element.getKey());
277                     }
278 
279                     elementEncoding.writeConstructor();
280                     elementEncoding.writeValue( element.getKey());
281 
282                     ITypeEncoding elementEncodingVal;
283                     elementEncodingVal = _encoder.getType( element.getValue()).getEncoding( element.getValue());
284                     //if (tmp is null)
285                     //{
286                     //    logError("getType error");
287                     //}
288                     //  elementEncodingVal = tmp.getEncoding(cast(String)element.getValue());
289                     elementEncodingVal.writeConstructor();
290                     elementEncodingVal.writeValue( element.getValue());
291 
292                     //elementEncoding = getEncoder().getType(element.getValue()).getEncoding(element.getValue());
293                     //elementEncoding.writeConstructor();
294                     //elementEncoding.writeValue(element.getValue());
295 
296                 }
297             } finally {
298                 setKeyEncoding(IfixedKeyType);
299             }
300 
301             //try {
302             //    while (iter.hasNext())
303             //    {
304             //       // Map.Entry<?, ?> element = iter.next();
305             //        TypeEncoding elementEncoding;
306             //
307             //        if (fixedKeyType is null)
308             //        {
309             //            elementEncoding = _encoder.getType(element.getKey()).getEncoding(element.getKey());
310             //        }
311             //        else
312             //        {
313             //            elementEncoding = fixedKeyType.getEncoding(element.getKey());
314             //        }
315             //
316             //        elementEncoding.writeConstructor();
317             //        elementEncoding.writeValue(element.getKey());
318             //        elementEncoding = getEncoder().getType(element.getValue()).getEncoding(element.getValue());
319             //        elementEncoding.writeConstructor();
320             //        elementEncoding.writeValue(element.getValue());
321             //    }
322             //} finally {
323             //    // Reset Existing key type encoding for later encode step or reuse until cleared by caller
324             //    setKeyEncoding(fixedKeyType);
325             //}
326         }
327 
328         override
329         protected int getEncodedValueSize(Map!(String,Object) val)
330         {
331             return 4 + ((val == _value) ? _length : calculateSize(val));
332         }
333 
334         override
335         public byte getEncodingCode()
336         {
337             return EncodingCodes.MAP32;
338         }
339 
340         override
341         public MapType getType()
342         {
343             return this.outer;
344         }
345 
346         override
347         public bool encodesSuperset(TypeEncoding!(Map!(String,Object)) encoding)
348         {
349             return (getType() == encoding.getType());
350         }
351 
352         override
353         public Object readValue()
354         {
355             DecoderImpl decoder = getDecoder();
356             ReadableBuffer buffer = decoder.getBuffer();
357 
358             int size = decoder.readRawInt();
359             // todo - limit the decoder with size
360             int count = decoder.readRawInt();
361             if (count > decoder.getByteBufferRemaining()) {
362                 throw new IllegalArgumentException("Map element count " ~ to!string(count) ~" is specified to be greater than the amount of data available ("~
363                                                    to!string(decoder.getByteBufferRemaining())~ ")");
364             }
365 
366             ITypeConstructor keyConstructor = null;
367             ITypeConstructor valueConstructor = null;
368 
369             Map!(String, Object) map = new LinkedHashMap!(String,Object)(count);
370             for(int i = 0; i < count / 2; i++)
371             {
372                 keyConstructor =  findNextDecoder(decoder, buffer, keyConstructor);
373                 if(keyConstructor is null)
374                 {
375                    // throw new DecodeException("Unknown constructor");
376                     logError("Unknown constructor");
377                     return null;
378                 }
379 
380                 String key = cast(String)keyConstructor.readValue();
381 
382                 bool arrayType = false;
383                 byte code = buffer.get(buffer.position());
384                 switch (code)
385                 {
386                     case EncodingCodes.ARRAY8:
387                          goto case;
388                     case EncodingCodes.ARRAY32:
389                         arrayType = true;
390                         break;
391                     default:
392                         break;
393                 }
394 
395                 valueConstructor = findNextDecoder(decoder, buffer, valueConstructor);
396                 if (valueConstructor is null)
397                 {
398                    // throw new DecodeException("Unknown constructor");
399                     logError("Unknown constructor");
400                     return null;
401                 }
402 
403                 Object value;
404 
405                 //if (arrayType)
406                 //{
407                 //    value = ((ArrayType.ArrayEncoding) valueConstructor).readValueArray();
408                 //}
409                 //else
410                 {
411                     value = valueConstructor.readValue();
412                 }
413 
414                 map.put(key, value);
415             }
416 
417             return cast(Object)map;
418         }
419 
420         override
421         public void skipValue()
422         {
423             DecoderImpl decoder = getDecoder();
424             ReadableBuffer buffer = decoder.getBuffer();
425             int size = decoder.readRawInt();
426             buffer.position(buffer.position() + size);
427         }
428 
429         public void setValue(Map!(String,Object) value, int length)
430         {
431             _value = value;
432             _length = length;
433         }
434 
435         override
436          void writeConstructor()
437          {
438             super.writeConstructor();
439          }
440 
441         override int getConstructorSize()
442         {
443             return super.getConstructorSize();
444         }
445 
446 
447     }
448 
449     class ShortMapEncoding
450             : SmallFloatingSizePrimitiveTypeEncoding!(Map!(String,Object))
451             , MapEncoding
452     {
453         private Map!(String,Object) _value;
454         private int _length;
455 
456         this(EncoderImpl encoder, DecoderImpl decoder)
457         {
458             super(encoder, decoder);
459         }
460 
461         override
462         protected void writeEncodedValue(Map!(String,Object) map)
463         {
464             getEncoder().getBuffer().ensureRemaining(getSizeBytes() + getEncodedValueSize(map));
465             getEncoder().writeRaw(cast(byte)(2 * map.size()));
466 
467            // Iterator<Map.Entry> iter = map.entrySet().iterator();
468 
469             IAMQPType IfixedKeyType = this.outer.fixedKeyType;
470 
471             // Clear existing fixed key type encoding to prevent application to nested Maps
472             setKeyEncoding(null);
473 
474             try
475             {
476                 foreach (MapEntry!(String, Object) element ; map)
477                 {
478                     ITypeEncoding elementEncoding;
479                     if (IfixedKeyType is null)
480                     {
481                         IAMQPType tmp = _encoder.getType( element.getKey());
482                         if (tmp is null)
483                         {
484                             logError( "getType error");
485                         }
486                         elementEncoding = tmp.getEncoding( element.getKey());
487                     }
488                     else
489                     {
490                         elementEncoding = IfixedKeyType.getEncoding( element.getKey());
491                     }
492 
493                     elementEncoding.writeConstructor();
494                     elementEncoding.writeValue( element.getKey());
495 
496                     elementEncoding = getEncoder().getType( element.getValue()).getEncoding( element.getValue());
497                     elementEncoding.writeConstructor();
498                     elementEncoding.writeValue( element.getValue());
499                 }
500 
501             } finally
502             {
503                 setKeyEncoding(IfixedKeyType);
504             }
505             //try {
506             //    while (iter.hasNext())
507             //    {
508             //        Map.Entry<?, ?> element = iter.next();
509             //        TypeEncoding elementEncoding;
510             //
511             //        if (fixedKeyType is null)
512             //        {
513             //            elementEncoding = _encoder.getType(element.getKey()).getEncoding(element.getKey());
514             //        }
515             //        else
516             //        {
517             //            elementEncoding = fixedKeyType.getEncoding(element.getKey());
518             //        }
519             //
520             //        elementEncoding.writeConstructor();
521             //        elementEncoding.writeValue(element.getKey());
522             //        elementEncoding = getEncoder().getType(element.getValue()).getEncoding(element.getValue());
523             //        elementEncoding.writeConstructor();
524             //        elementEncoding.writeValue(element.getValue());
525             //    }
526             //} finally {
527             //    // Reset Existing key type encoding for later encode step or reuse until cleared by caller
528             //    setKeyEncoding(fixedKeyType);
529             //}
530         }
531 
532         override
533         protected int getEncodedValueSize(Map!(String,Object) val)
534         {
535             return 1 + ((val == _value) ? _length : calculateSize(val));
536         }
537 
538         override
539         public byte getEncodingCode()
540         {
541             return EncodingCodes.MAP8;
542         }
543 
544         override
545         public MapType getType()
546         {
547             return this.outer;
548         }
549 
550         override
551         public bool encodesSuperset(TypeEncoding!(Map!(String,Object)) encoder)
552         {
553             return encoder == this;
554         }
555 
556         override
557         public Object readValue()
558         {
559             DecoderImpl decoder = getDecoder();
560             ReadableBuffer buffer = decoder.getBuffer();
561 
562             int size = (decoder.readRawByte()) & 0xff;
563             // todo - limit the decoder with size
564             int count = (decoder.readRawByte()) & 0xff;
565 
566             ITypeConstructor keyConstructor = null;
567             ITypeConstructor valueConstructor = null;
568 
569             Map!(String, Object) map = new LinkedHashMap!(String, Object)(count);
570             for(int i = 0; i < count / 2; i++)
571             {
572                 keyConstructor = findNextDecoder(decoder, buffer, keyConstructor);
573                 if(keyConstructor is null)
574                 {
575                     logError("Unknown constructor");
576                     return null;
577                    // throw new DecodeException("Unknown constructor");
578                 }
579 
580                 String key = cast(String)keyConstructor.readValue();
581 
582                 bool arrayType = false;
583                 byte code = buffer.get(buffer.position());
584                 switch (code)
585                 {
586                     case EncodingCodes.ARRAY8:
587                          goto case;
588                     case EncodingCodes.ARRAY32:
589                         arrayType = true;
590                         break;
591                     default:
592                         break;
593                 }
594 
595                 valueConstructor = findNextDecoder(decoder, buffer, valueConstructor);
596                 if(valueConstructor is null)
597                 {
598                     //throw new DecodeException("Unknown constructor");
599                     logError("Unknown constructor");
600                     return null;
601                 }
602 
603                 Object value;
604 
605                 //if (arrayType)
606                 //{
607                 //    value = ((ArrayType.ArrayEncoding) valueConstructor).readValueArray();
608                 //}
609                 //else
610                 {
611                     value  = valueConstructor.readValue();
612                 }
613 
614                 map.put(key, value);
615             }
616 
617             return cast(Object)map;
618         }
619 
620         override
621         public void skipValue()
622         {
623             DecoderImpl decoder = getDecoder();
624             ReadableBuffer buffer = decoder.getBuffer();
625             int size = (cast(int)decoder.readRawByte()) & 0xff;
626             buffer.position(buffer.position() + size);
627         }
628 
629         public void setValue(Map!(String,Object) value, int length)
630         {
631             _value = value;
632             _length = length;
633         }
634 
635         override
636         void writeConstructor()
637         {
638             super.writeConstructor();
639         }
640 
641         override int getConstructorSize()
642         {
643             return super.getConstructorSize();
644         }
645     }
646 }