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