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.ListType; 13 14 import hunt.proton.codec.DecoderImpl; 15 import hunt.proton.codec.EncoderImpl; 16 import hunt.proton.codec.PrimitiveTypeEncoding; 17 import hunt.proton.codec.AbstractPrimitiveType; 18 import hunt.proton.codec.LargeFloatingSizePrimitiveTypeEncoding; 19 import hunt.proton.codec.AMQPType; 20 import hunt.proton.codec.TypeEncoding; 21 import hunt.proton.codec.TypeConstructor; 22 import hunt.proton.codec.EncodingCodes; 23 import hunt.proton.codec.ReadableBuffer; 24 import hunt.proton.codec.SmallFloatingSizePrimitiveTypeEncoding; 25 import hunt.proton.codec.FixedSizePrimitiveTypeEncoding; 26 import hunt.proton.codec.NullType; 27 import hunt.proton.codec.BooleanType; 28 import hunt.proton.codec.ByteType; 29 import hunt.proton.codec.ShortType; 30 import hunt.proton.codec.IntegerType; 31 import hunt.proton.codec.LongType; 32 import hunt.proton.codec.FloatType; 33 import hunt.proton.codec.DoubleType; 34 import hunt.proton.codec.CharacterType; 35 36 import hunt.Boolean; 37 import hunt.collection.Collection; 38 import hunt.collection.Collections; 39 import hunt.collection.List; 40 import hunt.collection.ArrayList; 41 import hunt.Exceptions; 42 import hunt.Short; 43 import hunt.Integer; 44 import hunt.Long; 45 import hunt.Float; 46 import hunt.Double; 47 import hunt.logging.ConsoleLogger; 48 49 import std.conv : to; 50 51 interface ListEncoding : PrimitiveTypeEncoding!(List!Object) { 52 void setValue(List!Object value, int length); 53 Object readValueArray(); 54 } 55 56 class ListType : AbstractPrimitiveType!(List!Object) { 57 private ListEncoding _listEncoding; 58 private ListEncoding _shortListEncoding; 59 private ListEncoding _zeroListEncoding; 60 private EncoderImpl _encoder; 61 62 this(EncoderImpl encoder, DecoderImpl decoder) { 63 _encoder = encoder; 64 _listEncoding = new AllListEncoding(encoder, decoder); 65 _shortListEncoding = new ShortListEncoding(encoder, decoder); 66 _zeroListEncoding = new ZeroListEncoding(encoder, decoder); 67 encoder.register(typeid(List!Object), this); 68 decoder.register(this); 69 } 70 71 public TypeInfo getTypeClass() { 72 return typeid(List!Object); 73 } 74 75 public ITypeEncoding getEncoding(Object v) { 76 List!Object val = cast(List!Object) v; 77 int calculatedSize = calculateSize(val, _encoder); 78 ListEncoding encoding = val.isEmpty() ? _zeroListEncoding : (val.size() > 255 79 || calculatedSize >= 254) ? _listEncoding : _shortListEncoding; 80 81 encoding.setValue(val, calculatedSize); 82 return encoding; 83 } 84 85 // public static TypeInfo 86 private static List!Object decodeBooleanArray(BooleanEncoding constructor, int count) { 87 // boolean[] array = new boolean[count]; 88 List!Object array = new ArrayList!Object; 89 for (int i = 0; i < count; i++) { 90 array.add(new Boolean(constructor.readPrimitiveValue())); 91 } 92 93 return array; 94 } 95 96 private static List!Object decodeByteArray(ByteType.ByteEncoding constructor, int count) { 97 List!Object array = new ArrayList!Object; 98 99 for (int i = 0; i < count; i++) { 100 array.add(constructor.readPrimitiveValue()); 101 } 102 103 return array; 104 } 105 106 private static List!Object decodeShortArray(ShortType.ShortEncoding constructor, int count) { 107 List!Object array = new ArrayList!Object; 108 109 for (int i = 0; i < count; i++) { 110 array.add(new Short(constructor.readPrimitiveValue())); 111 } 112 113 return array; 114 } 115 116 private static List!Object decodeIntArray(IntegerType.IntegerEncoding constructor, int count) { 117 List!Object array = new ArrayList!Object; 118 119 for (int i = 0; i < count; i++) { 120 array.add(new Integer(constructor.readPrimitiveValue())); 121 } 122 123 return array; 124 } 125 126 private static List!Object decodeLongArray(LongType.LongEncoding constructor, int count) { 127 List!Object array = new ArrayList!Object; 128 129 for (int i = 0; i < count; i++) { 130 array.add(new Long(constructor.readPrimitiveValue())); 131 } 132 133 return array; 134 } 135 136 private static List!Object decodeFloatArray(FloatType.FloatEncoding constructor, int count) { 137 List!Object array = new ArrayList!Object; 138 for (int i = 0; i < count; i++) { 139 array.add(new Float(constructor.readPrimitiveValue())); 140 } 141 142 return array; 143 } 144 145 private static List!Object decodeDoubleArray(DoubleType.DoubleEncoding constructor, int count) { 146 List!Object array = new ArrayList!Object; 147 148 for (int i = 0; i < count; i++) { 149 array.add(new Double(constructor.readPrimitiveValue())); 150 } 151 152 return array; 153 } 154 155 private static List!Object decodeCharArray(CharacterType.CharacterEncoding constructor, 156 int count) { 157 List!Object array = new ArrayList!Object; 158 159 for (int i = 0; i < count; i++) { 160 array.add(constructor.readPrimitiveValue()); 161 } 162 163 return array; 164 } 165 166 private static Object decodeArrayAsObject(DecoderImpl decoder, int count) { 167 ITypeConstructor constructor = decoder.readConstructor(true); 168 if (constructor.encodesJavaPrimitive()) { 169 if (count > decoder.getByteBufferRemaining()) { 170 throw new IllegalArgumentException("Array element count error"); 171 } 172 173 if (cast(BooleanEncoding) constructor !is null) { 174 return cast(Object)(decodeBooleanArray(cast(BooleanEncoding) constructor, count)); 175 } else if (cast(ByteType.ByteEncoding) constructor !is null) { 176 return cast(Object)(decodeByteArray(cast(ByteType.ByteEncoding) constructor, count)); 177 } else if (cast(ShortType.ShortEncoding) constructor !is null) { 178 return cast(Object)(decodeShortArray(cast(ShortType.ShortEncoding) constructor, 179 count)); 180 } else if (cast(IntegerType.IntegerEncoding) constructor !is null) { 181 return cast(Object)(decodeIntArray(cast(IntegerType.IntegerEncoding) constructor, 182 count)); 183 } else if (cast(LongType.LongEncoding) constructor !is null) { 184 return cast(Object)(decodeLongArray(cast(LongType.LongEncoding) constructor, count)); 185 } else if (cast(FloatType.FloatEncoding) constructor !is null) { 186 return cast(Object)(decodeFloatArray(cast(FloatType.FloatEncoding) constructor, 187 count)); 188 } else if (cast(DoubleType.DoubleEncoding) constructor !is null) { 189 return cast(Object)(decodeDoubleArray(cast(DoubleType.DoubleEncoding) constructor, 190 count)); 191 } else if (cast(CharacterType.CharacterEncoding) constructor !is null) { 192 return cast(Object)(decodeCharArray(cast(CharacterType.CharacterEncoding) constructor, 193 count)); 194 } else { 195 throw new ClassCastException("Unexpected class "); 196 } 197 } else { 198 return cast(Object)(decodeNonPrimitive(decoder, constructor, count)); 199 } 200 } 201 202 private static List!Object decodeNonPrimitive(DecoderImpl decoder, 203 ITypeConstructor constructor, int count) { 204 if (count > decoder.getByteBufferRemaining()) { 205 throw new IllegalArgumentException("Array element count " ~ to!string( 206 count) ~ " is specified to be greater than the amount of data available (" ~ to!string( 207 decoder.getByteBufferRemaining()) ~ ")"); 208 } 209 210 if (cast(ShortListEncoding) constructor !is null) { 211 ShortListEncoding arrayEncoding = cast(ShortListEncoding) constructor; 212 213 // Object[] array = new Object[count]; 214 List!Object ob = new ArrayList!Object; 215 for (int i = 0; i < count; i++) { 216 ob.add(arrayEncoding.readValueArray()); 217 } 218 219 return ob; 220 } else if (cast(AllListEncoding) constructor !is null) { 221 AllListEncoding arrayEncoding = cast(AllListEncoding) constructor; 222 223 // Object[] array = new Object[count]; 224 List!Object ob = new ArrayList!Object; 225 for (int i = 0; i < count; i++) { 226 ob.add(arrayEncoding.readValueArray()); 227 } 228 229 return ob; 230 } else { 231 // Object[] array = (Object[]) Array.newInstance(constructor.getTypeClass(), count); 232 233 List!Object array = new ArrayList!Object; 234 for (int i = 0; i < count; i++) { 235 array.add(constructor.readValue()); 236 } 237 238 return array; 239 } 240 } 241 242 private static int calculateSize(List!Object val, EncoderImpl encoder) { 243 int len = 0; 244 int count = val.size(); 245 for (int i = 0; i < count; i++) { 246 Object element = val.get(i); 247 //logInfo("i : %d" , i ); 248 //if (element !is null) 249 //{ 250 // logInfo(" %s" , typeid(element).toString ); 251 //} 252 IAMQPType type = encoder.getType(element); 253 //if (cast( NullType)type !is null) 254 //{ 255 // logInfo("null!!!!!!!!!!!!!!!!!!!!!!"); 256 //} 257 if (type is null) { 258 logError("!!!!!!logError!!!!!!!!!!!!!!!!"); 259 // throw new IllegalArgumentException("No encoding defined for type: "); 260 } 261 ITypeEncoding elementEncoding = type.getEncoding(element); 262 263 //NullEncoding n = cast(NullEncoding)elementEncoding; 264 //if (n !is null) 265 //{ 266 // logInfo("n!!!!!!!!!!!!!!!!!!!!!!"); 267 //} 268 269 len += elementEncoding.getConstructorSize() + elementEncoding.getValueSize(element); 270 } 271 return len; 272 } 273 274 public ListEncoding getCanonicalEncoding() { 275 return _listEncoding; 276 } 277 278 public Collection!(TypeEncoding!(List!Object)) getAllEncodings() { 279 List!(TypeEncoding!(List!Object)) lst = new ArrayList!(TypeEncoding!(List!Object)); 280 lst.add(_zeroListEncoding); 281 lst.add(_shortListEncoding); 282 lst.add(_listEncoding); 283 return lst; 284 } 285 286 class AllListEncoding : LargeFloatingSizePrimitiveTypeEncoding!(List!Object), ListEncoding { 287 288 private List!Object _value; 289 private int _length; 290 291 this(EncoderImpl encoder, DecoderImpl decoder) { 292 super(encoder, decoder); 293 } 294 295 override protected void writeEncodedValue(List!Object val) { 296 getEncoder().getBuffer().ensureRemaining(getSizeBytes() + getEncodedValueSize(val)); 297 getEncoder().writeRaw(val.size()); 298 299 int count = val.size(); 300 301 for (int i = 0; i < count; i++) { 302 Object element = val.get(i); 303 ITypeEncoding elementEncoding = getEncoder().getType(element).getEncoding(element); 304 elementEncoding.writeConstructor(); 305 elementEncoding.writeValue(element); 306 } 307 } 308 309 override protected int getEncodedValueSize(List!Object val) { 310 return 4 + ((val == _value) ? _length : calculateSize(val, getEncoder())); 311 } 312 313 override public byte getEncodingCode() { 314 return EncodingCodes.LIST32; 315 } 316 317 public Object readValueArray() { 318 DecoderImpl decoder = getDecoder(); 319 int size = (cast(int) decoder.readRawByte()) & 0xFF; 320 int count = (cast(int) decoder.readRawByte()) & 0xFF; 321 return decodeArrayAsObject(decoder, count); 322 } 323 324 override public ListType getType() { 325 return this.outer; 326 } 327 328 override public bool encodesSuperset(TypeEncoding!(List!Object) encoding) { 329 return (getType() == encoding.getType()); 330 } 331 332 override public Object readValue() { 333 DecoderImpl decoder = getDecoder(); 334 ReadableBuffer buffer = decoder.getBuffer(); 335 336 int size = decoder.readRawInt(); 337 // todo - limit the decoder with size 338 int count = decoder.readRawInt(); 339 // Ensure we do not allocate an array of size greater then the available data, otherwise there is a risk for an OOM error 340 if (count > decoder.getByteBufferRemaining()) { 341 throw new IllegalArgumentException("List element count " ~ to!string( 342 count) ~ " is specified to be greater than the amount of data available (" ~ to!string( 343 decoder.getByteBufferRemaining()) ~ ")"); 344 } 345 346 ITypeConstructor typeConstructor = null; 347 348 List!(Object) list = new ArrayList!(Object)(count); 349 for (int i = 0; i < count; i++) { 350 bool arrayType = false; 351 byte encodingCode = buffer.get(buffer.position()); 352 switch (encodingCode) { 353 case EncodingCodes.ARRAY8: 354 goto case; 355 case EncodingCodes.ARRAY32: 356 arrayType = true; 357 break; 358 default: 359 break; 360 } 361 362 // Whenever we can just reuse the previously used TypeDecoder instead 363 // of spending time looking up the same one again. 364 if (typeConstructor is null) { 365 typeConstructor = getDecoder().readConstructor(); 366 } else { 367 if (encodingCode == EncodingCodes.DESCRIBED_TYPE_INDICATOR) { 368 typeConstructor = getDecoder().readConstructor(); 369 } else { 370 IPrimitiveTypeEncoding primitiveConstructor = cast(IPrimitiveTypeEncoding) typeConstructor; 371 if (encodingCode != primitiveConstructor.getEncodingCode()) { 372 typeConstructor = getDecoder().readConstructor(); 373 } else { 374 // consume the encoding code byte for real 375 encodingCode = buffer.get(); 376 } 377 } 378 } 379 380 if (typeConstructor is null) { 381 // throw new DecodeException("Unknown constructor"); 382 logError("Unknown constructor"); 383 } 384 385 version (HUNT_AMQP_DEBUG) tracef("Constructor: %s", typeid(typeConstructor)); 386 387 Object value; 388 389 if (arrayType) { 390 value = (cast(ListEncoding) typeConstructor).readValueArray(); 391 } else { 392 value = typeConstructor.readValue(); 393 } 394 list.add(value); 395 } 396 397 return cast(Object) list; 398 } 399 400 override public void skipValue() { 401 DecoderImpl decoder = getDecoder(); 402 ReadableBuffer buffer = decoder.getBuffer(); 403 int size = decoder.readRawInt(); 404 buffer.position(buffer.position() + size); 405 } 406 407 override public void setValue(List!Object value, int length) { 408 _value = value; 409 _length = length; 410 } 411 412 override void writeConstructor() { 413 super.writeConstructor(); 414 } 415 416 override int getConstructorSize() { 417 return super.getConstructorSize(); 418 } 419 420 } 421 422 class ShortListEncoding : SmallFloatingSizePrimitiveTypeEncoding!(List!Object), ListEncoding { 423 424 private List!Object _value; 425 private int _length; 426 427 this(EncoderImpl encoder, DecoderImpl decoder) { 428 super(encoder, decoder); 429 } 430 431 override protected void writeEncodedValue(List!Object val) { 432 getEncoder().getBuffer().ensureRemaining(getSizeBytes() + getEncodedValueSize(val)); 433 getEncoder().writeRaw(cast(byte) val.size()); 434 435 int count = val.size(); 436 437 for (int i = 0; i < count; i++) { 438 Object element = val.get(i); 439 ITypeEncoding elementEncoding = getEncoder().getType(element).getEncoding(element); 440 elementEncoding.writeConstructor(); 441 elementEncoding.writeValue(element); 442 } 443 } 444 445 override protected int getEncodedValueSize(List!Object val) { 446 return 1 + ((val == _value) ? _length : calculateSize(val, getEncoder())); 447 } 448 449 override public byte getEncodingCode() { 450 return EncodingCodes.LIST8; 451 } 452 453 override public ListType getType() { 454 return this.outer; 455 } 456 457 override public bool encodesSuperset(TypeEncoding!(List!Object) encoder) { 458 return encoder == this; 459 } 460 461 public Object readValueArray() { 462 DecoderImpl decoder = getDecoder(); 463 int size = (cast(int) decoder.readRawByte()) & 0xFF; 464 int count = (cast(int) decoder.readRawByte()) & 0xFF; 465 return decodeArrayAsObject(decoder, count); 466 } 467 468 override public Object readValue() { 469 DecoderImpl decoder = getDecoder(); 470 ReadableBuffer buffer = decoder.getBuffer(); 471 472 int size = (cast(int) decoder.readRawByte()) & 0xff; 473 // todo - limit the decoder with size 474 int count = (cast(int) decoder.readRawByte()) & 0xff; 475 476 ITypeConstructor typeConstructor = null; 477 478 List!(Object) list = new ArrayList!Object(count); 479 for (int i = 0; i < count; i++) { 480 bool arrayType = false; 481 byte encodingCode = buffer.get(buffer.position()); 482 switch (encodingCode) { 483 case EncodingCodes.ARRAY8: 484 goto case; 485 case EncodingCodes.ARRAY32: 486 arrayType = true; 487 break; 488 default: 489 break; 490 } 491 492 // Whenever we can just reuse the previously used TypeDecoder instead 493 // of spending time looking up the same one again. 494 if (typeConstructor is null) { 495 typeConstructor = getDecoder().readConstructor(); 496 } else { 497 //|| !(typeConstructor instanceof PrimitiveTypeEncoding<?>)) 498 if (encodingCode == EncodingCodes.DESCRIBED_TYPE_INDICATOR 499 || (cast(IPrimitiveTypeEncoding) typeConstructor is null)) { 500 typeConstructor = getDecoder().readConstructor(); 501 } else { 502 IPrimitiveTypeEncoding primitiveConstructor = cast(IPrimitiveTypeEncoding) typeConstructor; 503 if (encodingCode != primitiveConstructor.getEncodingCode()) { 504 typeConstructor = getDecoder().readConstructor(); 505 } else { 506 // consume the encoding code byte for real 507 encodingCode = buffer.get(); 508 } 509 } 510 } 511 512 if (typeConstructor is null) { 513 logError("Unknown constructor"); 514 // throw new DecodeException("Unknown constructor"); 515 } 516 517 Object value; 518 519 if (arrayType) { 520 value = (cast(ListEncoding) typeConstructor).readValueArray(); 521 } else { 522 value = typeConstructor.readValue(); 523 } 524 525 list.add(value); 526 } 527 528 return cast(Object) list; 529 } 530 531 override public void skipValue() { 532 DecoderImpl decoder = getDecoder(); 533 ReadableBuffer buffer = decoder.getBuffer(); 534 int size = (cast(int) decoder.readRawByte()) & 0xff; 535 buffer.position(buffer.position() + size); 536 } 537 538 override public void setValue(List!Object value, int length) { 539 _value = value; 540 _length = length; 541 } 542 543 override void writeConstructor() { 544 super.writeConstructor(); 545 } 546 547 override int getConstructorSize() { 548 return super.getConstructorSize(); 549 } 550 551 } 552 553 class ZeroListEncoding : FixedSizePrimitiveTypeEncoding!(List!Object), ListEncoding { 554 this(EncoderImpl encoder, DecoderImpl decoder) { 555 super(encoder, decoder); 556 } 557 558 override public byte getEncodingCode() { 559 return EncodingCodes.LIST0; 560 } 561 562 override protected int getFixedSize() { 563 return 0; 564 } 565 566 override public ListType getType() { 567 return this.outer; 568 } 569 570 Object readValueArray() { 571 return null; 572 } 573 574 override public void setValue(List!Object value, int length) { 575 } 576 577 override public void writeValue(Object val) { 578 } 579 580 override public bool encodesSuperset(TypeEncoding!(List!Object) encoder) { 581 return encoder == this; 582 } 583 584 override public Object readValue() { 585 return new ArrayList!Object; 586 } 587 588 override void writeConstructor() { 589 super.writeConstructor(); 590 } 591 592 override int getConstructorSize() { 593 return super.getConstructorSize(); 594 } 595 596 } 597 }