1 module hunt.proton.codec.ObjectMapType;
2 
3 import std.stdio;
4 
5 /*
6  * hunt-proton: AMQP Protocol library for D programming language.
7  *
8  * Copyright (C) 2018-2019 HuntLabs
9  *
10  * Website: https://www.huntlabs.net/
11  *
12  * Licensed under the Apache-2.0 License.
13  *
14  */
15 import hunt.proton.codec.PrimitiveTypeEncoding;
16 import hunt.proton.codec.AMQPType;
17 import hunt.proton.codec.EncoderImpl;
18 import hunt.proton.codec.DecoderImpl;
19 import hunt.proton.codec.AbstractPrimitiveType;
20 import hunt.collection.Collection;
21 import hunt.collection.Map;
22 import hunt.collection.ArrayList;
23 import hunt.proton.codec.LargeFloatingSizePrimitiveTypeEncoding;
24 import hunt.Exceptions;
25 import hunt.Object;
26 import hunt.collection.Map;
27 import hunt.String;
28 import hunt.proton.codec.TypeEncoding;
29 import hunt.proton.codec.EncodingCodes;
30 import hunt.proton.codec.StringType;
31 import hunt.collection.LinkedHashMap;
32 import hunt.proton.codec.TypeConstructor;
33 import hunt.proton.codec.ReadableBuffer;
34 import hunt.proton.codec.SmallFloatingSizePrimitiveTypeEncoding;
35 import hunt.logging;
36 import std.conv : to;
37 
38 interface MapEncoding : PrimitiveTypeEncoding!(Map!(Object,Object))
39 {
40     void setValue(Map!(Object,Object) value, int length);
41 }
42 
43 class ObjectMapType : AbstractPrimitiveType!(Map!(Object,Object))
44 {
45     private MapEncoding _mapEncoding;
46     private MapEncoding _shortMapEncoding;
47     private EncoderImpl _encoder;
48 
49     //private AMQPType<?> fixedKeyType;
50 
51     private IAMQPType  fixedKeyType;
52 
53 
54     this(EncoderImpl encoder, DecoderImpl decoder)
55     {
56         _encoder = encoder;
57         _mapEncoding = new AllMapEncoding(encoder, decoder);
58         _shortMapEncoding = new ShortMapEncoding(encoder, decoder);
59         encoder.register(typeid(LinkedHashMap!(Object,Object)), this);
60         decoder.register(this);
61     }
62 
63     public TypeInfo getTypeClass()
64     {
65         return typeid(LinkedHashMap!(Object,Object));
66     }
67 
68     public void setKeyEncoding(IAMQPType keyType)
69     {
70         this.fixedKeyType = keyType;
71     }
72 
73     public ITypeEncoding getEncoding(Object val)
74     {
75         int calculatedSize = calculateSize(cast(Map!(Object,Object))val);
76         MapEncoding encoding = ((cast(Map!(Object,Object))val).size() > 127 || calculatedSize >= 254)
77         ? _mapEncoding
78         : _shortMapEncoding;
79 
80         encoding.setValue(cast(Map!(Object,Object))val, calculatedSize);
81         return encoding;
82     }
83 
84     private int calculateSize(Map!(Object,Object) map)
85     {
86 
87         //implementationMissing(false);
88         int len = 0;
89 
90         //Iterator<Map.Entry<?, ?>> iter = map.entrySet().iterator();
91         IAMQPType IfixedKeyType = this.fixedKeyType;
92 
93         // Clear existing fixed key type encoding to prevent application to nested Maps
94         setKeyEncoding(null);
95 
96         try
97         {
98 
99             foreach (MapEntry!(Object, Object) element ; map)
100             {
101                 ITypeEncoding elementEncoding;
102                 if (IfixedKeyType is null)
103                 {
104                     IAMQPType tmp = _encoder.getType( element.getKey());
105                     if (tmp is null)
106                     {
107                         logError( "getType Error");
108                     }
109 
110                     elementEncoding = tmp.getEncoding( element.getKey());
111                     //  elementEncoding = _encoder.getType(element.getKey()).getEncoding(element.getKey());
112                 }
113                 else
114                 {
115                     elementEncoding = IfixedKeyType.getEncoding( element.getKey());
116                 }
117 
118                 len += elementEncoding.getConstructorSize() + elementEncoding.getValueSize( element.getKey());
119 
120 
121 
122 
123                 IAMQPType tmp = (_encoder.getType( element.getValue()));
124                 if (tmp is null)
125                 {
126                     logError( "getType Error");
127                 }
128                 ITypeEncoding  elementEncodingVal = tmp.getEncoding( element.getValue());
129                 len += elementEncodingVal.getConstructorSize() + elementEncodingVal.getValueSize( element.getValue());
130 
131 
132                 //if (cast(String)element.getValue() !is null)
133                 //{
134                 //    StringType tmp = cast(StringType)(_encoder.getType(element.getValue()));
135                 //    if (tmp is null)
136                 //    {
137                 //        logError("getType Error");
138                 //    }
139                 //
140                 //    StringEncoding  elementEncodingVal = tmp.getEncoding(cast(String)element.getValue());
141                 //    len += elementEncodingVal.getConstructorSize() + elementEncodingVal.getValueSize(cast(String)element.getValue());
142                 //}else
143                 //{
144                 //    logError("unknown type");
145                 //}
146             }
147         } finally {
148             setKeyEncoding(IfixedKeyType);
149         }
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!(Object,Object)))
225     public Collection!(TypeEncoding!(Map!(Object,Object))) getAllEncodings()
226     {
227         ArrayList!(TypeEncoding!(Map!(Object,Object))) lst = new ArrayList!(TypeEncoding!(Map!(Object,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!(Object,Object))
236     , MapEncoding
237     {
238 
239         private Map!(Object,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!(Object,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             {
261                 foreach (MapEntry!(Object, Object) element ; map)
262                 {
263                     ITypeEncoding elementEncoding;
264                     if (IfixedKeyType is null)
265                     {
266                         elementEncoding = (_encoder.getType( element.getKey())).getEncoding( element.getKey());
267                         if (elementEncoding is null)
268                         {
269                             logError( "getType Error");
270                         }
271 
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!(Object,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 ObjectMapType getType()
342         {
343             return this.outer;
344         }
345 
346         override
347         public bool encodesSuperset(TypeEncoding!(Map!(Object,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!(Object, Object) map = new LinkedHashMap!(Object,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                 Object key = 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!(Object,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!(Object,Object))
451     , MapEncoding
452     {
453         private Map!(Object,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!(Object,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!(Object, 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!(Object,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 ObjectMapType getType()
546         {
547             return this.outer;
548         }
549 
550         override
551         public bool encodesSuperset(TypeEncoding!(Map!(Object,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!(Object, Object) map = new LinkedHashMap!(Object, 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                 Object key = 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!(Object,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 }