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