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 module hunt.proton.codec.messaging.FastPathMessageAnnotationsType;
12 
13 import hunt.collection.Collection;
14 import hunt.collection.LinkedHashMap;
15 import hunt.collection.Map;
16 
17 import hunt.proton.codec.messaging.MessageAnnotationsType;
18 import hunt.proton.amqp.Symbol;
19 import hunt.proton.amqp.UnsignedLong;
20 import hunt.proton.amqp.messaging.MessageAnnotations;
21 import hunt.proton.codec.AMQPType;
22 import hunt.proton.codec.ArrayType;
23 import hunt.proton.codec.DecodeException;
24 import hunt.proton.codec.Decoder;
25 import hunt.proton.codec.DecoderImpl;
26 import hunt.proton.codec.EncoderImpl;
27 import hunt.proton.codec.EncodingCodes;
28 import hunt.proton.codec.FastPathDescribedTypeConstructor;
29 import hunt.proton.codec.PrimitiveTypeEncoding;
30 import hunt.proton.codec.ReadableBuffer;
31 import hunt.proton.codec.SymbolType;
32 import hunt.proton.codec.TypeConstructor;
33 import hunt.proton.codec.TypeEncoding;
34 import hunt.proton.codec.WritableBuffer;
35 import std.concurrency : initOnce;
36 import hunt.Exceptions;
37 import hunt.proton.codec.SymbolMapType;
38 import hunt.logging;
39 import std.conv:to;
40 
41 class FastPathMessageAnnotationsType : AMQPType!(MessageAnnotations), FastPathDescribedTypeConstructor!(MessageAnnotations) {
42 
43     private static byte DESCRIPTOR_CODE = 0x72;
44 
45     //private static Object[] DESCRIPTORS = {
46     //    UnsignedLong.valueOf(DESCRIPTOR_CODE), Symbol.valueOf("amqp:message-annotations:map"),
47     //};
48 
49     static Object[]  DESCRIPTORS() {
50         __gshared Object[]  inst;
51         return initOnce!inst([UnsignedLong.valueOf(DESCRIPTOR_CODE), Symbol.valueOf("amqp:message-annotations:map")]);
52     }
53 
54     private MessageAnnotationsType annotationsType;
55     private SymbolType symbolType;
56 
57     this(EncoderImpl encoder) {
58         this.annotationsType = new MessageAnnotationsType(encoder);
59         this.symbolType = cast(SymbolType) encoder.getTypeFromClass(typeid(Symbol));
60     }
61 
62     public EncoderImpl getEncoder() {
63         return annotationsType.getEncoder();
64     }
65 
66     public DecoderImpl getDecoder() {
67         return annotationsType.getDecoder();
68     }
69 
70     override
71     public bool encodesJavaPrimitive() {
72         return false;
73     }
74 
75     override
76     public TypeInfo getTypeClass() {
77         return typeid(MessageAnnotations);
78     }
79 
80     override
81     public ITypeEncoding getEncoding(Object val) {
82         return annotationsType.getEncoding(cast(MessageAnnotations)val);
83     }
84 
85     override
86     public TypeEncoding!(MessageAnnotations) getCanonicalEncoding() {
87         return annotationsType.getCanonicalEncoding();
88     }
89 
90     override
91     public  Collection!(TypeEncoding!(MessageAnnotations)) getAllEncodings() {
92         return annotationsType.getAllEncodings();
93     }
94 
95     override
96     public Object readValue() {
97         DecoderImpl decoder = getDecoder();
98         ReadableBuffer buffer = decoder.getBuffer();
99 
100         int size;
101         int count;
102 
103         byte encodingCode = buffer.get();
104 
105         switch (encodingCode) {
106             case EncodingCodes.MAP8:
107                 size = buffer.get() & 0xFF;
108                 count = buffer.get() & 0xFF;
109                 break;
110             case EncodingCodes.MAP32:
111                 size = buffer.getInt();
112                 count = buffer.getInt();
113                 break;
114             case EncodingCodes.NULL:
115                 return new MessageAnnotations(null);
116             default:
117             {
118                 logError("Expected Map type but found encoding:");
119                 return null;
120             }
121                // throw new ProtonException("Expected Map type but found encoding: " ~ encodingCode);
122         }
123 
124         if (count > buffer.remaining()) {
125             throw new IllegalArgumentException("Map element count "~ to!string(count) ~" is specified to be greater than the amount of data available ("~
126                                                to!string(decoder.getByteBufferRemaining())~")");
127         }
128 
129         ITypeConstructor  valueConstructor = null;
130 
131         Map!(Symbol, Object) map = new LinkedHashMap!(Symbol,Object)(count);
132         for(int i = 0; i < count / 2; i++) {
133             Symbol key = decoder.readSymbol(null);
134             if (key is null) {
135                 logError("String key in DeliveryAnnotations cannot be null");
136                 return null;
137               //  throw new DecodeException("String key in DeliveryAnnotations cannot be null");
138             }
139 
140             bool arrayType = false;
141             byte code = buffer.get(buffer.position());
142             switch (code)
143             {
144                 case EncodingCodes.ARRAY8:
145                      goto case;
146                 case EncodingCodes.ARRAY32:
147                     arrayType = true;
148                     break;
149                 default:
150                     break;
151             }
152 
153             valueConstructor = findNextDecoder(decoder, buffer, valueConstructor);
154 
155             Object value;
156 
157             //if (arrayType) {
158             //    value = ((ArrayType.ArrayEncoding) valueConstructor).readValueArray();
159             //} else {
160                 value = valueConstructor.readValue();
161            // }
162 
163             map.put(key, value);
164         }
165 
166         return new MessageAnnotations(map);
167     }
168 
169     override
170     public void skipValue() {
171         getDecoder().readConstructor().skipValue();
172         //implementationMissing(false);
173     }
174 
175     override
176     public void write(Object v) {
177 
178         MessageAnnotations val = cast(MessageAnnotations)v;
179       //  implementationMissing(false);
180 
181         WritableBuffer buffer = getEncoder().getBuffer();
182 
183         buffer.put(EncodingCodes.DESCRIBED_TYPE_INDICATOR);
184         buffer.put(EncodingCodes.SMALLULONG);
185         buffer.put(DESCRIPTOR_CODE);
186 
187         SymbolMapType mapType = cast(SymbolMapType) getEncoder().getType(cast(Object)val.getValue());
188 
189         mapType.setKeyEncoding(symbolType);
190         try {
191             mapType.write(cast(Object)val.getValue());
192         } finally {
193             mapType.setKeyEncoding(null);
194         }
195     }
196 
197     public static void register(Decoder decoder, EncoderImpl encoder) {
198         FastPathMessageAnnotationsType type = new FastPathMessageAnnotationsType(encoder);
199         //implementationMissing(false);
200         foreach(Object descriptor ; DESCRIPTORS) {
201             decoder.registerFastPath(descriptor, type);
202         }
203         encoder.register(type);
204     }
205 
206     private static ITypeConstructor findNextDecoder(DecoderImpl decoder, ReadableBuffer buffer, ITypeConstructor previousConstructor) {
207         if (previousConstructor is null) {
208             return decoder.readConstructor();
209         } else {
210             byte encodingCode = buffer.get(buffer.position());
211             if (encodingCode == EncodingCodes.DESCRIBED_TYPE_INDICATOR ) {
212                 previousConstructor = decoder.readConstructor();
213             } else {
214                 IPrimitiveTypeEncoding primitiveConstructor = cast(IPrimitiveTypeEncoding) previousConstructor;
215                 if (encodingCode != primitiveConstructor.getEncodingCode()) {
216                     previousConstructor = decoder.readConstructor();
217                 } else {
218                     // consume the encoding code byte for real
219                     encodingCode = buffer.get();
220                 }
221             }
222         }
223 
224         if (previousConstructor is null) {
225             logError("Unknown constructor found in Map encoding:");
226             return null;
227            // throw new DecodeException("Unknown constructor found in Map encoding: ");
228         }
229 
230         return previousConstructor;
231     }
232 }