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