1/** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20package org.apache.thrift.protocol; 21 22import haxe.io.Bytes; 23import haxe.io.BytesInput; 24import haxe.io.BytesOutput; 25import haxe.io.BytesBuffer; 26import haxe.ds.GenericStack; 27import haxe.Int32; 28import haxe.Int64; 29import haxe.Utf8; 30 31import org.apache.thrift.TException; 32import org.apache.thrift.transport.TTransport; 33import org.apache.thrift.helper.ZigZag; 34import org.apache.thrift.helper.BitConverter; 35 36 37/** 38* Compact protocol implementation for thrift. 39*/ 40class TCompactProtocol extends TRecursionTracker implements TProtocol { 41 42 private static var ANONYMOUS_STRUCT : TStruct = new TStruct(""); 43 private static var TSTOP : TField = new TField("", TType.STOP, 0); 44 45 private static inline var PROTOCOL_ID : Int = 0x82; 46 private static inline var VERSION : Int = 1; 47 private static inline var VERSION_MASK : Int = 0x1f; // 0001 1111 48 private static inline var TYPE_MASK : Int = 0xE0; // 1110 0000 49 private static inline var TYPE_BITS : Int = 0x07; // 0000 0111 50 private static inline var TYPE_SHIFT_AMOUNT : Int = 5; 51 52 53 private static var ttypeToCompactType = [ 54 TType.STOP => TCompactTypes.STOP, 55 TType.BOOL => TCompactTypes.BOOLEAN_TRUE, 56 TType.BYTE => TCompactTypes.BYTE, 57 TType.DOUBLE => TCompactTypes.DOUBLE, 58 TType.I16 => TCompactTypes.I16, 59 TType.I32 => TCompactTypes.I32, 60 TType.I64 => TCompactTypes.I64, 61 TType.STRING => TCompactTypes.BINARY, 62 TType.STRUCT => TCompactTypes.STRUCT, 63 TType.MAP => TCompactTypes.MAP, 64 TType.SET => TCompactTypes.SET, 65 TType.LIST => TCompactTypes.LIST 66 ]; 67 68 private static var tcompactTypeToType = [ 69 TCompactTypes.STOP => TType.STOP, 70 TCompactTypes.BOOLEAN_TRUE => TType.BOOL, 71 TCompactTypes.BOOLEAN_FALSE => TType.BOOL, 72 TCompactTypes.BYTE => TType.BYTE, 73 TCompactTypes.I16 => TType.I16, 74 TCompactTypes.I32 => TType.I32, 75 TCompactTypes.I64 => TType.I64, 76 TCompactTypes.DOUBLE => TType.DOUBLE, 77 TCompactTypes.BINARY => TType.STRING, 78 TCompactTypes.LIST => TType.LIST, 79 TCompactTypes.SET => TType.SET, 80 TCompactTypes.MAP => TType.MAP, 81 TCompactTypes.STRUCT => TType.STRUCT 82 ]; 83 84 85 /** 86 * Used to keep track of the last field for the current and previous structs, 87 * so we can do the delta stuff. 88 */ 89 private var lastField_ : GenericStack<Int> = new GenericStack<Int>(); 90 private var lastFieldId_ : Int = 0; 91 92 /** 93 * If we encounter a boolean field begin, save the TField here so it can 94 * have the value incorporated. 95 */ 96 private var booleanField_ : Null<TField>; 97 98 /** 99 * If we Read a field header, and it's a boolean field, save the boolean 100 * value here so that ReadBool can use it. 101 */ 102 private var boolValue_ : Null<Bool>; 103 104 105 // whether the underlying system holds Strings as UTF-8 106 // http://old.haxe.org/manual/encoding 107 private static var utf8Strings = haxe.Utf8.validate("Ç-ß-Æ-Ю-Ш"); 108 109 // the transport used 110 public var trans(default,null) : TTransport; 111 112 113 // TCompactProtocol Constructor 114 public function new( trans : TTransport) { 115 this.trans = trans; 116 } 117 118 public function getTransport() : TTransport { 119 return trans; 120 } 121 122 123 public function Reset() : Void{ 124 while ( ! lastField_.isEmpty()) { 125 lastField_.pop(); 126 } 127 lastFieldId_ = 0; 128 } 129 130 131 /** 132 * Writes a byte without any possibility of all that field header nonsense. 133 * Used internally by other writing methods that know they need to Write a byte. 134 */ 135 private function WriteByteDirect( b : Int) : Void { 136 var buf = Bytes.alloc(1); 137 buf.set( 0, b); 138 trans.write( buf, 0, 1); 139 } 140 141 /** 142 * Write an i32 as a varint. Results in 1-5 bytes on the wire. 143 */ 144 private function WriteVarint32( n : UInt) : Void { 145 var i32buf = new BytesBuffer(); 146 while (true) 147 { 148 if ((n & ~0x7F) == 0) 149 { 150 i32buf.addByte( n & 0xFF); 151 break; 152 } 153 else 154 { 155 i32buf.addByte( (n & 0x7F) | 0x80); 156 n >>= 7; 157 } 158 } 159 160 var tmp = i32buf.getBytes(); 161 trans.write( tmp, 0, tmp.length); 162 } 163 164 /** 165 * Write a message header to the wire. Compact Protocol messages contain the 166 * protocol version so we can migrate forwards in the future if need be. 167 */ 168 public function writeMessageBegin( message : TMessage) : Void { 169 Reset(); 170 171 var versionAndType : Int = (VERSION & VERSION_MASK) | ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK); 172 WriteByteDirect( PROTOCOL_ID); 173 WriteByteDirect( versionAndType); 174 WriteVarint32( cast( message.seqid, UInt)); 175 writeString( message.name); 176 } 177 178 /** 179 * Write a struct begin. This doesn't actually put anything on the wire. We 180 * use it as an opportunity to put special placeholder markers on the field 181 * stack so we can get the field id deltas correct. 182 */ 183 public function writeStructBegin(struct:TStruct) : Void { 184 lastField_.add( lastFieldId_); 185 lastFieldId_ = 0; 186 } 187 188 /** 189 * Write a struct end. This doesn't actually put anything on the wire. We use 190 * this as an opportunity to pop the last field from the current struct off 191 * of the field stack. 192 */ 193 public function writeStructEnd() : Void { 194 lastFieldId_ = lastField_.pop(); 195 } 196 197 /** 198 * Write a field header containing the field id and field type. If the 199 * difference between the current field id and the last one is small (< 15), 200 * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the 201 * field id will follow the type header as a zigzag varint. 202 */ 203 public function writeFieldBegin(field:TField) : Void { 204 if (field.type == TType.BOOL) 205 booleanField_ = field; // we want to possibly include the value, so we'll wait. 206 else 207 WriteFieldBeginInternal(field, 0xFF); 208 } 209 210 /** 211 * The workhorse of WriteFieldBegin. It has the option of doing a 212 * 'type override' of the type header. This is used specifically in the 213 * boolean field case. 214 */ 215 private function WriteFieldBeginInternal( field : TField, typeOverride : Int) : Void { 216 // if there's a type override, use that. 217 var typeToWrite : Int; 218 if ( typeOverride == 0xFF) 219 typeToWrite = getCompactType( field.type); 220 else 221 typeToWrite = typeOverride; 222 223 // check if we can use delta encoding for the field id 224 if (field.id > lastFieldId_ && field.id - lastFieldId_ <= 15) 225 { 226 // Write them together 227 WriteByteDirect((field.id - lastFieldId_) << 4 | typeToWrite); 228 } 229 else 230 { 231 // Write them separate 232 WriteByteDirect(typeToWrite); 233 writeI16(field.id); 234 } 235 236 lastFieldId_ = field.id; 237 } 238 239 /** 240 * Write the STOP symbol so we know there are no more fields in this struct. 241 */ 242 public function writeFieldStop() : Void { 243 WriteByteDirect( cast(TCompactTypes.STOP, Int)); 244 } 245 246 /** 247 * Write a map header. If the map is empty, omit the key and value type 248 * headers, as we don't need any additional information to skip it. 249 */ 250 public function writeMapBegin(map:TMap) : Void { 251 if (map.size == 0) 252 { 253 WriteByteDirect(0); 254 } 255 else 256 { 257 var kvtype = (getCompactType(map.keyType) << 4) | getCompactType(map.valueType); 258 WriteVarint32( cast( map.size, UInt)); 259 WriteByteDirect( kvtype); 260 } 261 } 262 263 /** 264 * Write a list header. 265 */ 266 public function writeListBegin( list : TList) : Void { 267 WriteCollectionBegin( list.elemType, list.size); 268 } 269 270 /** 271 * Write a set header. 272 */ 273 public function writeSetBegin( set : TSet) : Void { 274 WriteCollectionBegin( set.elemType, set.size); 275 } 276 277 /** 278 * Write a boolean value. Potentially, this could be a boolean field, in 279 * which case the field header info isn't written yet. If so, decide what the 280 * right type header is for the value and then Write the field header. 281 * Otherwise, Write a single byte. 282 */ 283 public function writeBool(b : Bool) : Void { 284 var bct : Int = b ? TCompactTypes.BOOLEAN_TRUE : TCompactTypes.BOOLEAN_FALSE; 285 286 if (booleanField_ != null) 287 { 288 // we haven't written the field header yet 289 WriteFieldBeginInternal( booleanField_, bct); 290 booleanField_ = null; 291 } 292 else 293 { 294 // we're not part of a field, so just Write the value. 295 WriteByteDirect( bct); 296 } 297 } 298 299 /** 300 * Write a byte. Nothing to see here! 301 */ 302 public function writeByte( b : Int) : Void { 303 WriteByteDirect( b); 304 } 305 306 /** 307 * Write an I16 as a zigzag varint. 308 */ 309 public function writeI16( i16 : Int) : Void { 310 WriteVarint32( ZigZag.FromInt( i16)); 311 } 312 313 /** 314 * Write an i32 as a zigzag varint. 315 */ 316 public function writeI32( i32 : Int) : Void { 317 WriteVarint32( ZigZag.FromInt( i32)); 318 } 319 320 /** 321 * Write an i64 as a zigzag varint. 322 */ 323 public function writeI64( i64 : haxe.Int64) : Void { 324 WriteVarint64( ZigZag.FromLong( i64)); 325 } 326 327 /** 328 * Write a double to the wire as 8 bytes. 329 */ 330 public function writeDouble( dub : Float) : Void { 331 var data = BitConverter.fixedLongToBytes( BitConverter.DoubleToInt64Bits(dub)); 332 trans.write( data, 0, data.length); 333 } 334 335 /** 336 * Write a string to the wire with a varint size preceding. 337 */ 338 public function writeString(str : String) : Void { 339 var buf = new BytesBuffer(); 340 if( utf8Strings) 341 buf.addString( str); // no need to encode on UTF8 targets, the string is just fine 342 else 343 buf.addString( Utf8.encode( str)); 344 var tmp = buf.getBytes(); 345 writeBinary( tmp); 346 } 347 348 /** 349 * Write a byte array, using a varint for the size. 350 */ 351 public function writeBinary( bin : Bytes) : Void { 352 WriteVarint32( cast(bin.length,UInt)); 353 trans.write( bin, 0, bin.length); 354 } 355 356 357 // These methods are called by structs, but don't actually have any wire 358 // output or purpose. 359 public function writeMessageEnd() : Void { } 360 public function writeMapEnd() : Void { } 361 public function writeListEnd() : Void { } 362 public function writeSetEnd() : Void { } 363 public function writeFieldEnd() : Void { } 364 365 // 366 // Internal writing methods 367 // 368 369 /** 370 * Abstract method for writing the start of lists and sets. List and sets on 371 * the wire differ only by the type indicator. 372 */ 373 private function WriteCollectionBegin( elemType : Int, size : Int) : Void { 374 if (size <= 14) { 375 WriteByteDirect( size << 4 | getCompactType(elemType)); 376 } 377 else { 378 WriteByteDirect( 0xf0 | getCompactType(elemType)); 379 WriteVarint32( cast(size, UInt)); 380 } 381 } 382 383 /** 384 * Write an i64 as a varint. Results in 1-10 bytes on the wire. 385 */ 386 private function WriteVarint64(n : haxe.Int64) : Void { 387 var varint64out = new BytesBuffer(); 388 while (true) 389 { 390 if( Int64.isZero( Int64.and( n, Int64.neg(Int64.make(0,0x7F))))) 391 { 392 #if( haxe_ver < 3.2) 393 varint64out.addByte( Int64.getLow(n)); 394 #else 395 varint64out.addByte( n.low); 396 #end 397 break; 398 } 399 else 400 { 401 #if ( haxe_ver < 3.2) 402 varint64out.addByte( (Int64.getLow(n) & 0x7F) | 0x80); 403 #else 404 varint64out.addByte( (n.low & 0x7F) | 0x80); 405 #end 406 n = Int64.shr( n, 7); 407 n = Int64.and( n, Int64.make(0x01FFFFFF,0xFFFFFFFF)); // clean out the shifted 7 bits 408 } 409 } 410 var tmp = varint64out.getBytes(); 411 trans.write( tmp, 0, tmp.length); 412 } 413 414 415 /** 416 * Read a message header. 417 */ 418 public function readMessageBegin():TMessage { 419 Reset(); 420 421 var protocolId : Int = readByte(); 422 if (protocolId != PROTOCOL_ID) { 423 throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected protocol id " + StringTools.hex(PROTOCOL_ID,2) + " but got " + StringTools.hex(protocolId)); 424 } 425 426 var versionAndType : Int = readByte(); 427 var version : Int = (versionAndType & VERSION_MASK); 428 if (version != VERSION) { 429 throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected version " + VERSION + " but got " + version); 430 } 431 432 var type : Int = ((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS); 433 var seqid : Int = cast( ReadVarint32(), Int); 434 var msgNm : String = readString(); 435 return new TMessage( msgNm, type, seqid); 436 } 437 438 /** 439 * Read a struct begin. There's nothing on the wire for this, but it is our 440 * opportunity to push a new struct begin marker onto the field stack. 441 */ 442 public function readStructBegin():TStruct { 443 lastField_.add(lastFieldId_); 444 lastFieldId_ = 0; 445 return ANONYMOUS_STRUCT; 446 } 447 448 /** 449 * Doesn't actually consume any wire data, just removes the last field for 450 * this struct from the field stack. 451 */ 452 public function readStructEnd() : Void { 453 // consume the last field we Read off the wire. 454 lastFieldId_ = lastField_.pop(); 455 } 456 457 /** 458 * Read a field header off the wire. 459 */ 460 public function readFieldBegin() : TField { 461 var type : Int = readByte(); 462 463 // if it's a stop, then we can return immediately, as the struct is over. 464 if (type == cast(TCompactTypes.STOP,Int)) { 465 return TSTOP; 466 } 467 468 var fieldId : Int; 469 470 // mask off the 4 MSB of the type header. it could contain a field id delta. 471 var modifier : Int = ((type & 0xf0) >> 4); 472 if (modifier == 0) 473 fieldId = readI16(); // not a delta. look ahead for the zigzag varint field id. 474 else 475 fieldId = lastFieldId_ + modifier; // add the delta to the last Read field id. 476 477 var field : TField = new TField( "", cast(getTType(type & 0x0f),Int), fieldId); 478 479 // if this happens to be a boolean field, the value is encoded in the type 480 if (isBoolType(type)) { 481 // save the boolean value in a special instance variable. 482 boolValue_ = ((type & 0x0f) == cast(TCompactTypes.BOOLEAN_TRUE,Int)); 483 } 484 485 // push the new field onto the field stack so we can keep the deltas going. 486 lastFieldId_ = field.id; 487 return field; 488 } 489 490 /** 491 * Read a map header off the wire. If the size is zero, skip Reading the key 492 * and value type. This means that 0-length maps will yield TMaps without the 493 * "correct" types. 494 */ 495 public function readMapBegin() : TMap { 496 var size : Int = cast( ReadVarint32(), Int); 497 var keyAndValueType : Int = ((size == 0) ? 0 : readByte()); 498 var key : Int = cast( getTType( (keyAndValueType & 0xF0) >> 4), Int); 499 var val : Int = cast( getTType( keyAndValueType & 0x0F), Int); 500 return new TMap( key, val, size); 501 } 502 503 /** 504 * Read a list header off the wire. If the list size is 0-14, the size will 505 * be packed into the element type header. If it's a longer list, the 4 MSB 506 * of the element type header will be 0xF, and a varint will follow with the 507 * true size. 508 */ 509 public function readListBegin():TList { 510 var size_and_type : Int = readByte(); 511 512 var size : Int = ((size_and_type & 0xF0) >> 4) & 0x0F; 513 if (size == 15) { 514 size = cast( ReadVarint32(), Int); 515 } 516 517 var type = getTType(size_and_type); 518 return new TList( type, size); 519 } 520 521 /** 522 * Read a set header off the wire. If the set size is 0-14, the size will 523 * be packed into the element type header. If it's a longer set, the 4 MSB 524 * of the element type header will be 0xF, and a varint will follow with the 525 * true size. 526 */ 527 public function readSetBegin() : TSet { 528 var size_and_type : Int = readByte(); 529 530 var size : Int = ((size_and_type & 0xF0) >> 4) & 0x0F; 531 if (size == 15) { 532 size = cast( ReadVarint32(), Int); 533 } 534 535 var type = getTType(size_and_type); 536 return new TSet( type, size); 537 } 538 539 /** 540 * Read a boolean off the wire. If this is a boolean field, the value should 541 * already have been Read during ReadFieldBegin, so we'll just consume the 542 * pre-stored value. Otherwise, Read a byte. 543 */ 544 public function readBool() : Bool { 545 if (boolValue_ != null) { 546 var result : Bool = boolValue_; 547 boolValue_ = null; 548 return result; 549 } 550 551 return (readByte() == cast(TCompactTypes.BOOLEAN_TRUE,Int)); 552 } 553 554 /** 555 * Read a single byte off the wire. Nothing interesting here. 556 */ 557 public function readByte() : Int { 558 var byteRawBuf = new BytesBuffer(); 559 trans.readAll( byteRawBuf, 0, 1); 560 return byteRawBuf.getBytes().get(0); 561 } 562 563 /** 564 * Read an i16 from the wire as a zigzag varint. 565 */ 566 public function readI16() : Int { 567 return ZigZag.ToInt( ReadVarint32()); 568 } 569 570 /** 571 * Read an i32 from the wire as a zigzag varint. 572 */ 573 public function readI32() : Int { 574 return ZigZag.ToInt( ReadVarint32()); 575 } 576 577 /** 578 * Read an i64 from the wire as a zigzag varint. 579 */ 580 public function readI64() : haxe.Int64 { 581 return ZigZag.ToLong( ReadVarint64()); 582 } 583 584 /** 585 * No magic here - just Read a double off the wire. 586 */ 587 public function readDouble():Float { 588 var longBits = new BytesBuffer(); 589 trans.readAll( longBits, 0, 8); 590 return BitConverter.Int64BitsToDouble( BitConverter.bytesToLong( longBits.getBytes())); 591 } 592 593 /** 594 * Reads a byte[] (via ReadBinary), and then UTF-8 decodes it. 595 */ 596 public function readString() : String { 597 var length : Int = cast( ReadVarint32(), Int); 598 599 if (length == 0) { 600 return ""; 601 } 602 603 var buf = new BytesBuffer(); 604 trans.readAll( buf, 0, length); 605 606 length = buf.length; 607 var inp = new BytesInput( buf.getBytes()); 608 var str = inp.readString( length); 609 if( utf8Strings) 610 return str; // no need to decode on UTF8 targets, the string is just fine 611 else 612 return Utf8.decode( str); 613 } 614 615 /** 616 * Read a byte[] from the wire. 617 */ 618 public function readBinary() : Bytes { 619 var length : Int = cast( ReadVarint32(), Int); 620 if (length == 0) { 621 return Bytes.alloc(0); 622 } 623 624 var buf = new BytesBuffer(); 625 trans.readAll( buf, 0, length); 626 return buf.getBytes(); 627 } 628 629 630 // These methods are here for the struct to call, but don't have any wire 631 // encoding. 632 public function readMessageEnd() : Void { } 633 public function readFieldEnd() : Void { } 634 public function readMapEnd() : Void { } 635 public function readListEnd() : Void { } 636 public function readSetEnd() : Void { } 637 638 // 639 // Internal Reading methods 640 // 641 642 /** 643 * Read an i32 from the wire as a varint. The MSB of each byte is set 644 * if there is another byte to follow. This can Read up to 5 bytes. 645 */ 646 private function ReadVarint32() : UInt { 647 var result : UInt = 0; 648 var shift : Int = 0; 649 while (true) { 650 var b : Int = readByte(); 651 result |= cast((b & 0x7f) << shift, UInt); 652 if ((b & 0x80) != 0x80) { 653 break; 654 } 655 shift += 7; 656 } 657 return result; 658 } 659 660 /** 661 * Read an i64 from the wire as a proper varint. The MSB of each byte is set 662 * if there is another byte to follow. This can Read up to 10 bytes. 663 */ 664 private function ReadVarint64() : Int64 { 665 var shift : Int = 0; 666 var result : Int64 = Int64.make(0,0); 667 while (true) { 668 var b : Int = readByte(); 669 result = Int64.or( result, Int64.shl( Int64.make(0,b & 0x7f), shift)); 670 if ((b & 0x80) != 0x80) { 671 break; 672 } 673 shift += 7; 674 } 675 676 return result; 677 } 678 679 680 // 681 // type testing and converting 682 // 683 684 private function isBoolType( b : Int) : Bool { 685 var lowerNibble : Int = b & 0x0f; 686 switch(lowerNibble) 687 { 688 case TCompactTypes.BOOLEAN_TRUE: return true; 689 case TCompactTypes.BOOLEAN_FALSE: return true; 690 default: return false; 691 } 692 } 693 694 695 /** 696 * Given a TCompactProtocol.TCompactTypes constant, convert it to its corresponding 697 * TType value. 698 */ 699 private function getTType( type : Int) : Int { 700 try 701 { 702 return tcompactTypeToType[type]; 703 } 704 catch ( e : Dynamic) 705 { 706 var tt : Int = (type & 0x0f); 707 throw new TProtocolException( TProtocolException.UNKNOWN, 'don\'t know what type: $tt ($e)'); 708 } 709 } 710 711 /** 712 * Given a TType value, find the appropriate TCompactProtocol.TCompactTypes constant. 713 */ 714 private function getCompactType( ttype : Int) : Int 715 { 716 return cast( ttypeToCompactType[ttype], Int); 717 } 718} 719