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 12 module hunt.proton.codec.messaging.FastPathPropertiesType; 13 14 import hunt.collection.Collection; 15 16 import hunt.proton.codec.messaging.PropertiesType; 17 import hunt.proton.amqp.Symbol; 18 import hunt.proton.amqp.UnsignedLong; 19 import hunt.proton.amqp.messaging.Properties; 20 import hunt.proton.codec.AMQPType; 21 import hunt.proton.codec.DecodeException; 22 import hunt.proton.codec.Decoder; 23 import hunt.proton.codec.DecoderImpl; 24 import hunt.proton.codec.EncoderImpl; 25 import hunt.proton.codec.EncodingCodes; 26 import hunt.proton.codec.FastPathDescribedTypeConstructor; 27 import hunt.proton.codec.ReadableBuffer; 28 import hunt.proton.codec.TypeEncoding; 29 import hunt.proton.codec.WritableBuffer; 30 import hunt.Exceptions; 31 import std.concurrency : initOnce; 32 import hunt.logging; 33 import std.conv : to; 34 import hunt.String; 35 36 class FastPathPropertiesType : AMQPType!(Properties), FastPathDescribedTypeConstructor!(Properties) { 37 38 private static byte DESCRIPTOR_CODE = 0x73; 39 40 //private static Object[] DESCRIPTORS = 41 //{ 42 // UnsignedLong.valueOf(DESCRIPTOR_CODE), Symbol.valueOf("amqp:properties:list"), 43 //}; 44 45 static Object[] DESCRIPTORS() { 46 __gshared Object[] inst; 47 return initOnce!inst([UnsignedLong.valueOf(DESCRIPTOR_CODE), Symbol.valueOf("amqp:properties:list")]); 48 } 49 50 51 private PropertiesType propertiesType; 52 53 this(EncoderImpl encoder) { 54 this.propertiesType = new PropertiesType(encoder); 55 } 56 57 public EncoderImpl getEncoder() { 58 return propertiesType.getEncoder(); 59 } 60 61 public DecoderImpl getDecoder() { 62 return propertiesType.getDecoder(); 63 } 64 65 override 66 public Properties readValue() { 67 DecoderImpl decoder = getDecoder(); 68 ReadableBuffer buffer = decoder.getBuffer(); 69 byte typeCode = decoder.getBuffer().get(); 70 71 int size = 0; 72 int count = 0; 73 74 switch (typeCode) { 75 case EncodingCodes.LIST0: 76 break; 77 case EncodingCodes.LIST8: 78 size = buffer.get() & 0xff; 79 count = buffer.get() & 0xff; 80 break; 81 case EncodingCodes.LIST32: 82 size = buffer.getInt(); 83 count = buffer.getInt(); 84 break; 85 default: 86 { 87 logError("Incorrect type found in Properties encoding: %d", typeCode); 88 break; 89 } 90 // throw new DecodeException("Incorrect type found in Properties encoding: " ~ typeCode); 91 } 92 93 Properties properties = new Properties(); 94 95 for (int index = 0; index < count; ++index) { 96 switch (index) { 97 case 0: 98 properties.setMessageId(cast(String)decoder.readObject()); 99 break; 100 case 1: 101 properties.setUserId(decoder.readBinary(null)); 102 break; 103 case 2: 104 properties.setTo(decoder.readString(null)); 105 break; 106 case 3: 107 properties.setSubject(decoder.readString(null)); 108 break; 109 case 4: 110 properties.setReplyTo(decoder.readString(null)); 111 break; 112 case 5: 113 properties.setCorrelationId(cast(String)decoder.readObject()); 114 break; 115 case 6: 116 properties.setContentType(decoder.readSymbol(null)); 117 break; 118 case 7: 119 properties.setContentEncoding(decoder.readSymbol(null)); 120 break; 121 case 8: 122 properties.setAbsoluteExpiryTime(decoder.readTimestamp(null)); 123 break; 124 case 9: 125 properties.setCreationTime(decoder.readTimestamp(null)); 126 break; 127 case 10: 128 properties.setGroupId(decoder.readString(null)); 129 break; 130 case 11: 131 properties.setGroupSequence(decoder.readUnsignedInteger(null)); 132 break; 133 case 12: 134 properties.setReplyToGroupId(decoder.readString(null)); 135 break; 136 default: 137 throw new IllegalStateException("To many entries in Properties encoding"); 138 } 139 } 140 141 return properties; 142 } 143 144 override 145 public void skipValue() { 146 // implementationMissing(false); 147 getDecoder().readConstructor().skipValue(); 148 } 149 150 override 151 public bool encodesJavaPrimitive() { 152 return false; 153 } 154 155 override 156 public TypeInfo getTypeClass() { 157 return typeid(Properties); 158 } 159 160 override 161 public ITypeEncoding getEncoding(Object properties) { 162 return propertiesType.getEncoding(cast(Properties)properties); 163 } 164 165 override 166 public TypeEncoding!(Properties) getCanonicalEncoding() { 167 return propertiesType.getCanonicalEncoding(); 168 } 169 170 override 171 public Collection!(TypeEncoding!(Properties)) getAllEncodings() { 172 return propertiesType.getAllEncodings(); 173 } 174 175 override 176 public void write(Object v) { 177 Properties value = cast(Properties) v; 178 WritableBuffer buffer = getEncoder().getBuffer(); 179 int count = getElementCount(value); 180 byte encodingCode = deduceEncodingCode(value, count); 181 182 buffer.put(EncodingCodes.DESCRIBED_TYPE_INDICATOR); 183 buffer.put(EncodingCodes.SMALLULONG); 184 buffer.put(DESCRIPTOR_CODE); 185 buffer.put(encodingCode); 186 187 // Optimized step, no other data to be written. 188 if (encodingCode == EncodingCodes.LIST0) { 189 return; 190 } 191 192 int fieldWidth; 193 194 if (encodingCode == EncodingCodes.LIST8) { 195 fieldWidth = 1; 196 } else { 197 fieldWidth = 4; 198 } 199 200 int startIndex = buffer.position(); 201 202 // Reserve space for the size and write the count of list elements. 203 if (fieldWidth == 1) { 204 buffer.put(cast(byte) 0); 205 buffer.put(cast(byte) count); 206 } else { 207 buffer.putInt(0); 208 buffer.putInt(count); 209 } 210 211 // Write the list elements and then compute total size written. 212 for (int i = 0; i < count; ++i) { 213 writeElement(value, i); 214 } 215 216 // Move back and write the size 217 int endIndex = buffer.position(); 218 int writeSize = endIndex - startIndex - fieldWidth; 219 220 buffer.position(startIndex); 221 if (fieldWidth == 1) { 222 buffer.put(cast(byte) writeSize); 223 } else { 224 buffer.putInt(writeSize); 225 } 226 buffer.position(endIndex); 227 } 228 229 private byte deduceEncodingCode(Properties value, int elementCount) { 230 if (elementCount == 0) { 231 return EncodingCodes.LIST0; 232 } else { 233 return EncodingCodes.LIST32; 234 } 235 } 236 237 private void writeElement(Properties properties, int index) { 238 switch (index) { 239 case 0: 240 getEncoder().writeObject(properties.getMessageId()); 241 break; 242 case 1: 243 getEncoder().writeBinary(properties.getUserId()); 244 break; 245 case 2: 246 getEncoder().writeString(properties.getTo()); 247 break; 248 case 3: 249 getEncoder().writeString(properties.getSubject()); 250 break; 251 case 4: 252 getEncoder().writeString(properties.getReplyTo()); 253 break; 254 case 5: 255 getEncoder().writeObject(properties.getCorrelationId()); 256 break; 257 case 6: 258 getEncoder().writeSymbol(properties.getContentType()); 259 break; 260 case 7: 261 getEncoder().writeSymbol(properties.getContentEncoding()); 262 break; 263 case 8: 264 getEncoder().writeTimestamp(properties.getAbsoluteExpiryTime()); 265 break; 266 case 9: 267 getEncoder().writeTimestamp(properties.getCreationTime()); 268 break; 269 case 10: 270 getEncoder().writeString(properties.getGroupId()); 271 break; 272 case 11: 273 getEncoder().writeUnsignedInteger(properties.getGroupSequence()); 274 break; 275 case 12: 276 getEncoder().writeString(properties.getReplyToGroupId()); 277 break; 278 default: 279 throw new IllegalArgumentException("Unknown Properties value index: " ~ to!string(index)); 280 } 281 } 282 283 private int getElementCount(Properties properties) { 284 if (properties.getReplyToGroupId() !is null) { 285 return 13; 286 } else if (properties.getGroupSequence() !is null) { 287 return 12; 288 } else if (properties.getGroupId() !is null) { 289 return 11; 290 } else if (properties.getCreationTime() !is null) { 291 return 10; 292 } else if (properties.getAbsoluteExpiryTime() !is null) { 293 return 9; 294 } else if (properties.getContentEncoding() !is null) { 295 return 8; 296 } else if (properties.getContentType() !is null) { 297 return 7; 298 } else if (properties.getCorrelationId() !is null) { 299 return 6; 300 } else if (properties.getReplyTo() !is null) { 301 return 5; 302 } else if (properties.getSubject() !is null) { 303 return 4; 304 } else if (properties.getTo() !is null) { 305 return 3; 306 } else if (properties.getUserId() !is null) { 307 return 2; 308 } else if (properties.getMessageId() !is null) { 309 return 1; 310 } 311 312 return 0; 313 } 314 315 public static void register(Decoder decoder, EncoderImpl encoder) { 316 FastPathPropertiesType type = new FastPathPropertiesType(encoder); 317 //implementationMissing(false); 318 foreach(Object descriptor ; DESCRIPTORS) { 319 decoder.registerFastPath(descriptor, type); 320 } 321 encoder.register(type); 322 } 323 }