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 }