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 }