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.io.Encoding; 27import haxe.ds.GenericStack; 28import haxe.crypto.Base64; 29import haxe.Int64; 30 31import uuid.Uuid; 32 33import org.apache.thrift.TException; 34import org.apache.thrift.protocol.TMessage; 35import org.apache.thrift.protocol.TField; 36import org.apache.thrift.protocol.TMap; 37import org.apache.thrift.protocol.TSet; 38import org.apache.thrift.protocol.TList; 39import org.apache.thrift.transport.TTransport; 40import org.apache.thrift.helper.UuidHelper; 41 42 43 44/* JSON protocol implementation for thrift. 45* This is a full-featured protocol supporting Write and Read. 46* 47* Please see the C++ class header for a detailed description of the wire format. 48* 49* Adapted from the Java version. 50*/ 51class TJSONProtocol extends TProtocolImplBase implements TProtocol { 52 53 // Stack of nested contexts that we may be in 54 private var contextStack : GenericStack<JSONBaseContext> = new GenericStack<JSONBaseContext>(); 55 56 // Current context that we are in 57 private var context : JSONBaseContext; 58 59 // Reader that manages a 1-byte buffer 60 private var reader : LookaheadReader; 61 62 // TJSONProtocol Constructor 63 public function new( transport : TTransport) 64 { 65 super(transport); 66 this.context = new JSONBaseContext(this); 67 this.reader = new LookaheadReader(this); 68 } 69 70 public function writeMessageBegin(message:TMessage) : Void { 71 WriteJSONArrayStart(); 72 WriteJSONInteger( JSONConstants.VERSION); 73 WriteJSONString( BytesFromString(message.name)); 74 WriteJSONInteger( message.type); 75 WriteJSONInteger( message.seqid); 76 } 77 78 public function writeMessageEnd() : Void { 79 WriteJSONArrayEnd(); 80 } 81 82 public function writeStructBegin(struct:TStruct) : Void { 83 WriteJSONObjectStart(); 84 } 85 86 public function writeStructEnd() : Void { 87 WriteJSONObjectEnd(); 88 } 89 90 public function writeFieldBegin(field:TField) : Void { 91 WriteJSONInteger( field.id ); 92 WriteJSONObjectStart(); 93 WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( field.type))); 94 } 95 96 public function writeFieldEnd() : Void { 97 WriteJSONObjectEnd(); 98 } 99 100 public function writeFieldStop() : Void { } 101 102 public function writeMapBegin(map:TMap) : Void { 103 WriteJSONArrayStart(); 104 WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( map.keyType))); 105 WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( map.valueType))); 106 WriteJSONInteger( map.size); 107 WriteJSONObjectStart(); 108 } 109 110 public function writeMapEnd() : Void { 111 WriteJSONObjectEnd(); 112 WriteJSONArrayEnd(); 113 } 114 115 public function writeListBegin(list:TList) : Void { 116 WriteJSONArrayStart(); 117 WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( list.elemType ))); 118 WriteJSONInteger( list.size); 119 } 120 121 public function writeListEnd() : Void { 122 WriteJSONArrayEnd(); 123 } 124 125 public function writeSetBegin(set:TSet) : Void { 126 WriteJSONArrayStart(); 127 WriteJSONString( BytesFromString( JSONConstants.GetTypeNameForTypeID( set.elemType))); 128 WriteJSONInteger( set.size); 129 } 130 131 public function writeSetEnd() : Void { 132 WriteJSONArrayEnd(); 133 } 134 135 public function writeBool(b : Bool) : Void { 136 if( b) 137 WriteJSONInteger( 1); 138 else 139 WriteJSONInteger( 0); 140 } 141 142 public function writeByte(b : Int) : Void { 143 WriteJSONInteger( b); 144 } 145 146 public function writeI16(i16 : Int) : Void { 147 WriteJSONInteger( i16); 148 } 149 150 public function writeI32(i32 : Int) : Void { 151 WriteJSONInteger( i32); 152 } 153 154 public function writeI64(i64 : haxe.Int64) : Void { 155 WriteJSONInt64( i64); 156 } 157 158 public function writeDouble(dub:Float) : Void { 159 WriteJSONDouble(dub); 160 } 161 162 public function writeString(str : String) : Void { 163 WriteJSONString( BytesFromString(str)); 164 } 165 166 public function writeBinary(bin:Bytes) : Void { 167 WriteJSONBase64(bin); 168 } 169 170 public function writeUuid(uuid : String) : Void { 171 writeString( UuidHelper.CanonicalUuid(uuid)); 172 } 173 174 public function readMessageBegin():TMessage { 175 var message : TMessage = new TMessage(); 176 ReadJSONArrayStart(); 177 if (ReadJSONInteger() != JSONConstants.VERSION) 178 { 179 throw new TProtocolException(TProtocolException.BAD_VERSION, 180 "Message contained bad version."); 181 } 182 183 message.name = ReadJSONString(false); 184 message.type = ReadJSONInteger(); 185 message.seqid = ReadJSONInteger(); 186 return message; 187 } 188 189 public function readMessageEnd() : Void { 190 ReadJSONArrayEnd(); 191 } 192 193 public function readStructBegin():TStruct { 194 ReadJSONObjectStart(); 195 return new TStruct(); 196 } 197 198 public function readStructEnd() : Void { 199 ReadJSONObjectEnd(); 200 } 201 202 public function readFieldBegin() : TField { 203 var field : TField = new TField(); 204 var ch = reader.Peek(); 205 if (StringFromBytes(ch) == JSONConstants.RBRACE) 206 { 207 field.type = TType.STOP; 208 } 209 else 210 { 211 field.id = ReadJSONInteger(); 212 ReadJSONObjectStart(); 213 field.type = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false)); 214 } 215 return field; 216 } 217 218 public function readFieldEnd() : Void { 219 ReadJSONObjectEnd(); 220 } 221 222 public function readMapBegin() : TMap { 223 ReadJSONArrayStart(); 224 var KeyType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false)); 225 var ValueType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false)); 226 var Count : Int = ReadJSONInteger(); 227 ReadJSONObjectStart(); 228 229 var map = new TMap( KeyType, ValueType, Count); 230 CheckReadBytesAvailableMap(map); 231 return map; 232 } 233 234 public function readMapEnd() : Void { 235 ReadJSONObjectEnd(); 236 ReadJSONArrayEnd(); 237 } 238 239 public function readListBegin():TList { 240 ReadJSONArrayStart(); 241 var ElementType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false)); 242 var Count : Int = ReadJSONInteger(); 243 244 var list = new TList( ElementType, Count); 245 CheckReadBytesAvailableList(list); 246 return list; 247 } 248 249 public function readListEnd() : Void { 250 ReadJSONArrayEnd(); 251 } 252 253 public function readSetBegin() : TSet { 254 ReadJSONArrayStart(); 255 var ElementType = JSONConstants.GetTypeIDForTypeName( ReadJSONString(false)); 256 var Count : Int = ReadJSONInteger(); 257 258 var set = new TSet( ElementType, Count); 259 CheckReadBytesAvailableSet(set); 260 return set; 261 } 262 263 public function readSetEnd() : Void { 264 ReadJSONArrayEnd(); 265 } 266 267 public function readBool() : Bool { 268 return (ReadJSONInteger() != 0); 269 } 270 271 public function readByte() : Int { 272 return ReadJSONInteger(); 273 } 274 275 public function readI16() : Int { 276 return ReadJSONInteger(); 277 } 278 279 public function readI32() : Int { 280 return ReadJSONInteger(); 281 } 282 283 public function readI64() : haxe.Int64 { 284 return ReadJSONInt64(); 285 } 286 287 public function readDouble():Float { 288 return ReadJSONDouble(); 289 } 290 291 public function readString() : String { 292 return ReadJSONString(false); 293 } 294 295 public function readBinary() : Bytes { 296 return ReadJSONBase64(); 297 } 298 299 public function readUuid() : String { 300 return UuidHelper.CanonicalUuid( readString()); 301 } 302 303 // Push a new JSON context onto the stack. 304 private function PushContext(c : JSONBaseContext) : Void { 305 contextStack.add(context); 306 context = c; 307 } 308 309 // Pop the last JSON context off the stack 310 private function PopContext() : Void { 311 context = contextStack.pop(); 312 } 313 314 315 // Write the bytes in array buf as a JSON characters, escaping as needed 316 private function WriteJSONString( b : Bytes) : Void { 317 context.Write(); 318 319 var tmp = BytesFromString( JSONConstants.QUOTE); 320 Transport.write( tmp, 0, tmp.length); 321 322 for (i in 0 ... b.length) { 323 var value = b.get(i); 324 325 if ((value & 0x00FF) >= 0x30) 326 { 327 if (String.fromCharCode(value) == JSONConstants.BACKSLASH.charAt(0)) 328 { 329 tmp = BytesFromString( JSONConstants.BACKSLASH + JSONConstants.BACKSLASH); 330 Transport.write( tmp, 0, tmp.length); 331 } 332 else 333 { 334 Transport.write( b, i, 1); 335 } 336 } 337 else 338 { 339 var num = JSONConstants.JSON_CHAR_TABLE[value]; 340 if (num == 1) 341 { 342 Transport.write( b, i, 1); 343 } 344 else if (num > 1) 345 { 346 var buf = new BytesBuffer(); 347 buf.addString( JSONConstants.BACKSLASH); 348 buf.addByte( num); 349 tmp = buf.getBytes(); 350 Transport.write( tmp, 0, tmp.length); 351 } 352 else 353 { 354 var buf = new BytesBuffer(); 355 buf.addString( JSONConstants.ESCSEQ); 356 buf.addString( HexChar( (value & 0xFF000000) >> 12)); 357 buf.addString( HexChar( (value & 0x00FF0000) >> 8)); 358 buf.addString( HexChar( (value & 0x0000FF00) >> 4)); 359 buf.addString( HexChar( value & 0x000000FF)); 360 tmp = buf.getBytes(); 361 Transport.write( tmp, 0, tmp.length); 362 } 363 } 364 } 365 366 tmp = BytesFromString( JSONConstants.QUOTE); 367 Transport.write( tmp, 0, tmp.length); 368 } 369 370 // Write out number as a JSON value. If the context dictates so, 371 // it will be wrapped in quotes to output as a JSON string. 372 private function WriteJSONInteger( num : Int) : Void { 373 context.Write(); 374 375 var str : String = ""; 376 var escapeNum : Bool = context.EscapeNumbers(); 377 378 if (escapeNum) { 379 str += JSONConstants.QUOTE; 380 } 381 382 str += Std.string(num); 383 384 if (escapeNum) { 385 str += JSONConstants.QUOTE; 386 } 387 388 var tmp = BytesFromString( str); 389 Transport.write( tmp, 0, tmp.length); 390 } 391 392 // Write out number as a JSON value. If the context dictates so, 393 // it will be wrapped in quotes to output as a JSON string. 394 private function WriteJSONInt64( num : Int64) : Void { 395 context.Write(); 396 397 var str : String = ""; 398 var escapeNum : Bool = context.EscapeNumbers(); 399 400 if (escapeNum) { 401 str += JSONConstants.QUOTE; 402 } 403 404 str += Std.string(num); 405 406 if (escapeNum) { 407 str += JSONConstants.QUOTE; 408 } 409 410 var tmp = BytesFromString( str); 411 Transport.write( tmp, 0, tmp.length); 412 } 413 414 // Write out a double as a JSON value. If it is NaN or infinity or if the 415 // context dictates escaping, Write out as JSON string. 416 private function WriteJSONDouble(num : Float) : Void { 417 context.Write(); 418 419 420 var special : Bool = false; 421 var rendered : String = ""; 422 if( Math.isNaN(num)) { 423 special = true; 424 rendered = JSONConstants.FLOAT_IS_NAN; 425 } else if (! Math.isFinite(num)) { 426 special = true; 427 if( num > 0) { 428 rendered = JSONConstants.FLOAT_IS_POS_INF; 429 } else { 430 rendered = JSONConstants.FLOAT_IS_NEG_INF; 431 } 432 } else { 433 rendered = Std.string(num); // plain and simple float number 434 } 435 436 // compose output 437 var escapeNum : Bool = special || context.EscapeNumbers(); 438 var str : String = ""; 439 if (escapeNum) { 440 str += JSONConstants.QUOTE; 441 } 442 str += rendered; 443 if (escapeNum) { 444 str += JSONConstants.QUOTE; 445 } 446 447 var tmp = BytesFromString( str); 448 Transport.write( tmp, 0, tmp.length); 449 } 450 451 // Write out contents of byte array b as a JSON string with base-64 encoded data 452 private function WriteJSONBase64( b : Bytes) : Void { 453 context.Write(); 454 455 var buf = new BytesBuffer(); 456 buf.addString( JSONConstants.QUOTE); 457 buf.addString( Base64.encode(b)); 458 buf.addString( JSONConstants.QUOTE); 459 460 var tmp = buf.getBytes(); 461 Transport.write( tmp, 0, tmp.length); 462 } 463 464 private function WriteJSONObjectStart() : Void { 465 context.Write(); 466 var tmp = BytesFromString( JSONConstants.LBRACE); 467 Transport.write( tmp, 0, tmp.length); 468 PushContext( new JSONPairContext(this)); 469 } 470 471 private function WriteJSONObjectEnd() : Void { 472 PopContext(); 473 var tmp = BytesFromString( JSONConstants.RBRACE); 474 Transport.write( tmp, 0, tmp.length); 475 } 476 477 private function WriteJSONArrayStart() : Void { 478 context.Write(); 479 var tmp = BytesFromString( JSONConstants.LBRACKET); 480 Transport.write( tmp, 0, tmp.length); 481 PushContext( new JSONListContext(this)); 482 } 483 484 private function WriteJSONArrayEnd() : Void { 485 PopContext(); 486 var tmp = BytesFromString( JSONConstants.RBRACKET); 487 Transport.write( tmp, 0, tmp.length); 488 } 489 490 491 /** 492 * Reading methods. 493 */ 494 495 // Read a byte that must match char, otherwise an exception is thrown. 496 public function ReadJSONSyntaxChar( char : String) : Void { 497 var b = BytesFromString( char); 498 499 var ch = reader.Read(); 500 if (ch.get(0) != b.get(0)) 501 { 502 throw new TProtocolException(TProtocolException.INVALID_DATA, 503 'Unexpected character: $ch'); 504 } 505 } 506 507 // Read in a JSON string, unescaping as appropriate. 508 // Skip Reading from the context if skipContext is true. 509 private function ReadJSONString(skipContext : Bool) : String 510 { 511 if (!skipContext) 512 { 513 context.Read(); 514 } 515 516 var buffer : BytesBuffer = new BytesBuffer(); 517 518 ReadJSONSyntaxChar( JSONConstants.QUOTE); 519 while (true) 520 { 521 var ch = reader.Read(); 522 523 // end of string? 524 if (StringFromBytes(ch) == JSONConstants.QUOTE) 525 { 526 break; 527 } 528 529 // escaped? 530 if (StringFromBytes(ch) != JSONConstants.ESCSEQ.charAt(0)) 531 { 532 buffer.addByte( ch.get(0)); 533 continue; 534 } 535 536 // distinguish between \uXXXX (hex unicode) and \X (control chars) 537 ch = reader.Read(); 538 if (StringFromBytes(ch) != JSONConstants.ESCSEQ.charAt(1)) 539 { 540 var value = JSONConstants.ESCAPE_CHARS_TO_VALUES[ch.get(0)]; 541 if( value == null) 542 { 543 throw new TProtocolException( TProtocolException.INVALID_DATA, "Expected control char"); 544 } 545 buffer.addByte( value); 546 continue; 547 } 548 549 550 // it's \uXXXX 551 var hexbuf = new BytesBuffer(); 552 var hexlen = Transport.readAll( hexbuf, 0, 4); 553 if( hexlen != 4) 554 { 555 throw new TProtocolException( TProtocolException.INVALID_DATA, "Not enough data for \\uNNNN sequence"); 556 } 557 558 var hexdigits = hexbuf.getBytes(); 559 var charcode = 0; 560 charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(0))); 561 charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(1))); 562 charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(2))); 563 charcode = (charcode << 4) + HexVal( String.fromCharCode(hexdigits.get(3))); 564 buffer.addString( String.fromCharCode(charcode)); 565 } 566 567 return StringFromBytes( buffer.getBytes()); 568 } 569 570 // Return true if the given byte could be a valid part of a JSON number. 571 private function IsJSONNumeric(b : Int) : Bool { 572 switch (b) 573 { 574 case "+".code: return true; 575 case "-".code: return true; 576 case ".".code: return true; 577 case "0".code: return true; 578 case "1".code: return true; 579 case "2".code: return true; 580 case "3".code: return true; 581 case "4".code: return true; 582 case "5".code: return true; 583 case "6".code: return true; 584 case "7".code: return true; 585 case "8".code: return true; 586 case "9".code: return true; 587 case "E".code: return true; 588 case "e".code: return true; 589 } 590 return false; 591 } 592 593 // Read in a sequence of characters that are all valid in JSON numbers. Does 594 // not do a complete regex check to validate that this is actually a number. 595 private function ReadJSONNumericChars() : String 596 { 597 var buffer : BytesBuffer = new BytesBuffer(); 598 while (true) 599 { 600 var ch = reader.Peek(); 601 if( ! IsJSONNumeric( ch.get(0))) 602 { 603 break; 604 } 605 buffer.addByte( reader.Read().get(0)); 606 } 607 return StringFromBytes( buffer.getBytes()); 608 } 609 610 // Read in a JSON number. If the context dictates, Read in enclosing quotes. 611 private function ReadJSONInteger() : Int { 612 context.Read(); 613 614 if (context.EscapeNumbers()) { 615 ReadJSONSyntaxChar( JSONConstants.QUOTE); 616 } 617 618 var str : String = ReadJSONNumericChars(); 619 620 if (context.EscapeNumbers()) { 621 ReadJSONSyntaxChar( JSONConstants.QUOTE); 622 } 623 624 var value = Std.parseInt(str); 625 if( value == null) { 626 throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str'); 627 } 628 629 return value; 630 } 631 632 // Read in a JSON number. If the context dictates, Read in enclosing quotes. 633 private function ReadJSONInt64() : haxe.Int64 { 634 context.Read(); 635 636 if (context.EscapeNumbers()) { 637 ReadJSONSyntaxChar( JSONConstants.QUOTE); 638 } 639 640 var str : String = ReadJSONNumericChars(); 641 if( str.length == 0) { 642 throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str'); 643 } 644 645 if (context.EscapeNumbers()) { 646 ReadJSONSyntaxChar( JSONConstants.QUOTE); 647 } 648 649 // process sign 650 var bMinus = false; 651 var startAt = 0; 652 if( (str.charAt(0) == "+") || (str.charAt(0) == "-")) { 653 bMinus = (str.charAt(0) == "-"); 654 startAt++; 655 } 656 657 // process digits 658 var value : Int64 = Int64.make(0,0); 659 var bGotDigits = false; 660 for( i in startAt ... str.length) { 661 var ch = str.charAt(i); 662 var digit = JSONConstants.DECIMAL_DIGITS[ch]; 663 if( digit == null) { 664 throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str'); 665 } 666 bGotDigits = true; 667 668 // these are decimal digits 669 value = Int64.mul( value, Int64.make(0,10)); 670 value = Int64.add( value, Int64.make(0,digit)); 671 } 672 673 // process pending minus sign, if applicable 674 // this should also handle the edge case MIN_INT64 correctly 675 if( bMinus && (Int64.compare(value,Int64.make(0,0)) > 0)) { 676 value = Int64.neg( value); 677 bMinus = false; 678 } 679 680 if( ! bGotDigits) { 681 throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str'); 682 } 683 684 return value; 685 } 686 687 // Read in a JSON double value. Throw if the value is not wrapped in quotes 688 // when expected or if wrapped in quotes when not expected. 689 private function ReadJSONDouble() : Float { 690 context.Read(); 691 692 var str : String = ""; 693 if (StringFromBytes(reader.Peek()) == JSONConstants.QUOTE) { 694 str = ReadJSONString(true); 695 696 // special cases 697 if( str == JSONConstants.FLOAT_IS_NAN) { 698 return Math.NaN; 699 } 700 if( str == JSONConstants.FLOAT_IS_POS_INF) { 701 return Math.POSITIVE_INFINITY; 702 } 703 if( str == JSONConstants.FLOAT_IS_NEG_INF) { 704 return Math.NEGATIVE_INFINITY; 705 } 706 707 if( ! context.EscapeNumbers()) { 708 // throw - we should not be in a string in this case 709 throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted"); 710 } 711 } 712 else 713 { 714 if( context.EscapeNumbers()) { 715 // This will throw - we should have had a quote if EscapeNumbers() == true 716 ReadJSONSyntaxChar( JSONConstants.QUOTE); 717 } 718 719 str = ReadJSONNumericChars(); 720 } 721 722 // parse and check - we should have at least one valid digit 723 var dub = Std.parseFloat( str); 724 if( (str.length == 0) || Math.isNaN(dub)) { 725 throw new TProtocolException(TProtocolException.INVALID_DATA, 'Bad numeric data: $str'); 726 } 727 728 return dub; 729 } 730 731 // Read in a JSON string containing base-64 encoded data and decode it. 732 private function ReadJSONBase64() : Bytes 733 { 734 var str = ReadJSONString(false); 735 return Base64.decode( str); 736 } 737 738 private function ReadJSONObjectStart() : Void { 739 context.Read(); 740 ReadJSONSyntaxChar( JSONConstants.LBRACE); 741 PushContext(new JSONPairContext(this)); 742 } 743 744 private function ReadJSONObjectEnd() : Void { 745 ReadJSONSyntaxChar( JSONConstants.RBRACE); 746 PopContext(); 747 } 748 749 private function ReadJSONArrayStart() : Void { 750 context.Read(); 751 ReadJSONSyntaxChar( JSONConstants.LBRACKET); 752 PushContext(new JSONListContext(this)); 753 } 754 755 private function ReadJSONArrayEnd() : Void { 756 ReadJSONSyntaxChar( JSONConstants.RBRACKET); 757 PopContext(); 758 } 759 760 761 public static function BytesFromString( str : String) : Bytes { 762 var buf = new BytesBuffer(); 763 buf.addString( str, Encoding.UTF8); 764 return buf.getBytes(); 765 } 766 767 public static function StringFromBytes( buf : Bytes) : String { 768 var inp = new BytesInput( buf); 769 if( buf.length == 0) 770 return ""; // readString() would return null in that case, which is wrong 771 return inp.readString( buf.length, Encoding.UTF8); 772 } 773 774 // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its corresponding hex value 775 private static function HexVal(char : String) : Int { 776 var value = JSONConstants.HEX_DIGITS[char]; 777 if( value == null) { 778 throw new TProtocolException(TProtocolException.INVALID_DATA, 'Expected hex character: $char'); 779 } 780 return value; 781 } 782 783 // Convert a byte containing a hex nibble to its corresponding hex character 784 private static function HexChar(nibble : Int) : String 785 { 786 return "0123456789abcdef".charAt(nibble & 0x0F); 787 } 788 789 790 // Return the minimum number of bytes a type will consume on the wire 791 public override function GetMinSerializedSize(type : TType) : Int 792 { 793 switch (type) 794 { 795 case TType.STOP: return 0; 796 case TType.VOID_: return 0; 797 case TType.BOOL: return 1; // written as int 798 case TType.BYTE: return 1; 799 case TType.DOUBLE: return 1; 800 case TType.I16: return 1; 801 case TType.I32: return 1; 802 case TType.I64: return 1; 803 case TType.STRING: return 2; // empty string 804 case TType.STRUCT: return 2; // empty struct 805 case TType.MAP: return 2; // empty map 806 case TType.SET: return 2; // empty set 807 case TType.LIST: return 2; // empty list 808 case TType.UUID: return 36; // "E236974D-F0B0-4E05-8F29-0B455D41B1A1" 809 default: throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "unrecognized type code"); 810 } 811 } 812 813} 814 815 816@:allow(TJSONProtocol) 817class JSONConstants { 818 public static var COMMA = ","; 819 public static var COLON = ":"; 820 public static var LBRACE = "{"; 821 public static var RBRACE = "}"; 822 public static var LBRACKET = "["; 823 public static var RBRACKET = "]"; 824 public static var QUOTE = "\""; 825 public static var BACKSLASH = "\\"; 826 827 public static var ESCSEQ = "\\u"; 828 829 public static var FLOAT_IS_NAN = "NaN"; 830 public static var FLOAT_IS_POS_INF = "Infinity"; 831 public static var FLOAT_IS_NEG_INF = "-Infinity"; 832 833 public static var VERSION = 1; 834 public static var JSON_CHAR_TABLE = [ 835 0, 0, 0, 0, 0, 0, 0, 0, 836 "b".code, "t".code, "n".code, 0, "f".code, "r".code, 0, 0, 837 0, 0, 0, 0, 0, 0, 0, 0, 838 0, 0, 0, 0, 0, 0, 0, 0, 839 1, 1, "\"".code, 1, 1, 1, 1, 1, 840 1, 1, 1, 1, 1, 1, 1, 1, 841 ]; 842 843 public static var ESCAPE_CHARS = ['"','\\','/','b','f','n','r','t']; 844 public static var ESCAPE_CHARS_TO_VALUES = [ 845 "\"".code => 0x22, 846 "\\".code => 0x5C, 847 "/".code => 0x2F, 848 "b".code => 0x08, 849 "f".code => 0x0C, 850 "n".code => 0x0A, 851 "r".code => 0x0D, 852 "t".code => 0x09 853 ]; 854 855 public static var DECIMAL_DIGITS = [ 856 "0" => 0, 857 "1" => 1, 858 "2" => 2, 859 "3" => 3, 860 "4" => 4, 861 "5" => 5, 862 "6" => 6, 863 "7" => 7, 864 "8" => 8, 865 "9" => 9 866 ]; 867 868 public static var HEX_DIGITS = [ 869 "0" => 0, 870 "1" => 1, 871 "2" => 2, 872 "3" => 3, 873 "4" => 4, 874 "5" => 5, 875 "6" => 6, 876 "7" => 7, 877 "8" => 8, 878 "9" => 9, 879 "A" => 10, 880 "a" => 10, 881 "B" => 11, 882 "b" => 11, 883 "C" => 12, 884 "c" => 12, 885 "D" => 13, 886 "d" => 13, 887 "E" => 14, 888 "e" => 14, 889 "F" => 15, 890 "f" => 15 891 ]; 892 893 894 public static var DEF_STRING_SIZE = 16; 895 896 public static var NAME_BOOL = 'tf'; 897 public static var NAME_BYTE = 'i8'; 898 public static var NAME_I16 = 'i16'; 899 public static var NAME_I32 = 'i32'; 900 public static var NAME_I64 = 'i64'; 901 public static var NAME_DOUBLE = 'dbl'; 902 public static var NAME_STRUCT = 'rec'; 903 public static var NAME_STRING = 'str'; 904 public static var NAME_MAP = 'map'; 905 public static var NAME_LIST = 'lst'; 906 public static var NAME_SET = 'set'; 907 public static var NAME_UUID = 'uid'; 908 909 public static function GetTypeNameForTypeID(typeID : Int) : String { 910 switch (typeID) 911 { 912 case TType.BOOL: return NAME_BOOL; 913 case TType.BYTE: return NAME_BYTE; 914 case TType.I16: return NAME_I16; 915 case TType.I32: return NAME_I32; 916 case TType.I64: return NAME_I64; 917 case TType.DOUBLE: return NAME_DOUBLE; 918 case TType.STRING: return NAME_STRING; 919 case TType.STRUCT: return NAME_STRUCT; 920 case TType.MAP: return NAME_MAP; 921 case TType.SET: return NAME_SET; 922 case TType.LIST: return NAME_LIST; 923 case TType.UUID: return NAME_UUID; 924 } 925 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized type"); 926 } 927 928 private static var NAMES_TO_TYPES = [ 929 NAME_BOOL => TType.BOOL, 930 NAME_BYTE => TType.BYTE, 931 NAME_I16 => TType.I16, 932 NAME_I32 => TType.I32, 933 NAME_I64 => TType.I64, 934 NAME_DOUBLE => TType.DOUBLE, 935 NAME_STRING => TType.STRING, 936 NAME_STRUCT => TType.STRUCT, 937 NAME_MAP => TType.MAP, 938 NAME_SET => TType.SET, 939 NAME_LIST => TType.LIST, 940 NAME_UUID => TType.UUID 941 ]; 942 943 public static function GetTypeIDForTypeName(name : String) : Int 944 { 945 var type = NAMES_TO_TYPES[name]; 946 if( null != type) { 947 return type; 948 } 949 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, "Unrecognized type"); 950 } 951 952} 953 954 955// Base class for tracking JSON contexts that may require inserting/Reading 956// additional JSON syntax characters. This base context does nothing. 957@:allow(TJSONProtocol) 958class JSONBaseContext 959{ 960 private var proto : TJSONProtocol; 961 962 public function new(proto : TJSONProtocol ) 963 { 964 this.proto = proto; 965 } 966 967 public function Write() : Void { } 968 public function Read() : Void { } 969 970 public function EscapeNumbers() : Bool { 971 return false; 972 } 973} 974 975 976// Context for JSON lists. 977// Will insert/Read commas before each item except for the first one 978@:allow(TJSONProtocol) 979class JSONListContext extends JSONBaseContext 980{ 981 public function new( proto : TJSONProtocol) { 982 super(proto); 983 } 984 985 private var first : Bool = true; 986 987 public override function Write() : Void { 988 if (first) 989 { 990 first = false; 991 } 992 else 993 { 994 var buf = new BytesBuffer(); 995 buf.addString( JSONConstants.COMMA); 996 var tmp = buf.getBytes(); 997 proto.Transport.write( tmp, 0, tmp.length); 998 } 999 } 1000 1001 public override function Read() : Void { 1002 if (first) 1003 { 1004 first = false; 1005 } 1006 else 1007 { 1008 proto.ReadJSONSyntaxChar( JSONConstants.COMMA); 1009 } 1010 } 1011} 1012 1013 1014// Context for JSON records. 1015// Will insert/Read colons before the value portion of each record 1016// pair, and commas before each key except the first. In addition, 1017// will indicate that numbers in the key position need to be escaped 1018// in quotes (since JSON keys must be strings). 1019@:allow(TJSONProtocol) 1020class JSONPairContext extends JSONBaseContext 1021{ 1022 public function new( proto : TJSONProtocol ) { 1023 super( proto); 1024 } 1025 1026 private var first : Bool = true; 1027 private var colon : Bool = true; 1028 1029 public override function Write() : Void { 1030 if (first) 1031 { 1032 first = false; 1033 colon = true; 1034 } 1035 else 1036 { 1037 var buf = new BytesBuffer(); 1038 buf.addString( colon ? JSONConstants.COLON : JSONConstants.COMMA); 1039 var tmp = buf.getBytes(); 1040 proto.Transport.write( tmp, 0, tmp.length); 1041 colon = !colon; 1042 } 1043 } 1044 1045 public override function Read() : Void { 1046 if (first) 1047 { 1048 first = false; 1049 colon = true; 1050 } 1051 else 1052 { 1053 proto.ReadJSONSyntaxChar( colon ? JSONConstants.COLON : JSONConstants.COMMA); 1054 colon = !colon; 1055 } 1056 } 1057 1058 public override function EscapeNumbers() : Bool 1059 { 1060 return colon; 1061 } 1062} 1063 1064// Holds up to one byte from the transport 1065@:allow(TJSONProtocol) 1066class LookaheadReader { 1067 1068 private var proto : TJSONProtocol; 1069 private var data : Bytes; 1070 1071 public function new( proto : TJSONProtocol ) { 1072 this.proto = proto; 1073 data = null; 1074 } 1075 1076 1077 // Return and consume the next byte to be Read, either taking it from the 1078 // data buffer if present or getting it from the transport otherwise. 1079 public function Read() : Bytes { 1080 var retval = Peek(); 1081 data = null; 1082 return retval; 1083 } 1084 1085 // Return the next byte to be Read without consuming, filling the data 1086 // buffer if it has not been filled alReady. 1087 public function Peek() : Bytes { 1088 if (data == null) { 1089 var buf = new BytesBuffer(); 1090 proto.Transport.readAll(buf, 0, 1); 1091 data = buf.getBytes(); 1092 } 1093 return data; 1094 } 1095} 1096 1097