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 }