blob: 896dfccc19545200ecc8ac031651b3892be70422 [file] [log] [blame]
Jake Farrell27274222011-11-10 20:32:44 +00001(*
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
20{$SCOPEDENUMS ON}
21
22unit Thrift.Protocol.JSON;
23
24interface
25
26uses
Phongphan Phutthaa6509f72015-10-31 01:09:47 +070027 Character,
Jake Farrell27274222011-11-10 20:32:44 +000028 Classes,
29 SysUtils,
30 Math,
Jake Farrell27274222011-11-10 20:32:44 +000031 Generics.Collections,
32 Thrift.Transport,
Jens Geyerd8bddbc2014-12-14 00:41:33 +010033 Thrift.Protocol,
34 Thrift.Utils;
Jake Farrell27274222011-11-10 20:32:44 +000035
36type
37 IJSONProtocol = interface( IProtocol)
38 ['{F0DAFDBD-692A-4B71-9736-F5D485A2178F}']
39 // Read a byte that must match b; otherwise an exception is thrown.
40 procedure ReadJSONSyntaxChar( b : Byte);
41 end;
42
43 // JSON protocol implementation for thrift.
44 // This is a full-featured protocol supporting Write and Read.
45 // Please see the C++ class header for a detailed description of the protocol's wire format.
46 // Adapted from the C# version.
47 TJSONProtocolImpl = class( TProtocolImpl, IJSONProtocol)
48 public
49 type
50 TFactory = class( TInterfacedObject, IProtocolFactory)
51 public
Roger Meier333bbf32012-01-08 21:51:08 +000052 function GetProtocol( const trans: ITransport): IProtocol;
Jake Farrell27274222011-11-10 20:32:44 +000053 end;
54
55 private
56 class function GetTypeNameForTypeID(typeID : TType) : string;
57 class function GetTypeIDForTypeName( const name : string) : TType;
58
59 protected
60 type
61 // Base class for tracking JSON contexts that may require
62 // inserting/Reading additional JSON syntax characters.
63 // This base context does nothing.
64 TJSONBaseContext = class
65 protected
Roger Meierfebe8452012-06-06 10:32:24 +000066 FProto : Pointer; // weak IJSONProtocol;
Jake Farrell27274222011-11-10 20:32:44 +000067 public
68 constructor Create( const aProto : IJSONProtocol);
69 procedure Write; virtual;
70 procedure Read; virtual;
71 function EscapeNumbers : Boolean; virtual;
72 end;
73
74 // Context for JSON lists.
75 // Will insert/Read commas before each item except for the first one.
76 TJSONListContext = class( TJSONBaseContext)
77 private
78 FFirst : Boolean;
79 public
80 constructor Create( const aProto : IJSONProtocol);
81 procedure Write; override;
82 procedure Read; override;
83 end;
84
85 // Context for JSON records. Will insert/Read colons before the value portion of each record
86 // pair, and commas before each key except the first. In addition, will indicate that numbers
87 // in the key position need to be escaped in quotes (since JSON keys must be strings).
88 TJSONPairContext = class( TJSONBaseContext)
89 private
90 FFirst, FColon : Boolean;
91 public
92 constructor Create( const aProto : IJSONProtocol);
93 procedure Write; override;
94 procedure Read; override;
95 function EscapeNumbers : Boolean; override;
96 end;
97
98 // Holds up to one byte from the transport
99 TLookaheadReader = class
100 protected
Roger Meierfebe8452012-06-06 10:32:24 +0000101 FProto : Pointer; // weak IJSONProtocol;
Jake Farrell27274222011-11-10 20:32:44 +0000102 constructor Create( const aProto : IJSONProtocol);
103
104 private
105 FHasData : Boolean;
106 FData : TBytes;
107
108 public
109 // Return and consume the next byte to be Read, either taking it from the
110 // data buffer if present or getting it from the transport otherwise.
111 function Read : Byte;
112
113 // Return the next byte to be Read without consuming, filling the data
114 // buffer if it has not been filled alReady.
115 function Peek : Byte;
116 end;
117
118 protected
119 // Stack of nested contexts that we may be in
120 FContextStack : TStack<TJSONBaseContext>;
121
122 // Current context that we are in
123 FContext : TJSONBaseContext;
124
125 // Reader that manages a 1-byte buffer
126 FReader : TLookaheadReader;
127
128 // Push/pop a new JSON context onto/from the stack.
Roger Meier45a37262012-01-08 21:44:44 +0000129 procedure ResetContextStack;
Roger Meier333bbf32012-01-08 21:51:08 +0000130 procedure PushContext( const aCtx : TJSONBaseContext);
Jake Farrell27274222011-11-10 20:32:44 +0000131 procedure PopContext;
132
133 public
134 // TJSONProtocolImpl Constructor
Roger Meier333bbf32012-01-08 21:51:08 +0000135 constructor Create( const aTrans : ITransport);
Jake Farrell27274222011-11-10 20:32:44 +0000136 destructor Destroy; override;
137
138 protected
139 // IJSONProtocol
140 // Read a byte that must match b; otherwise an exception is thrown.
141 procedure ReadJSONSyntaxChar( b : Byte);
142
143 private
144 // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its corresponding hex value
145 class function HexVal( ch : Byte) : Byte;
146
147 // Convert a byte containing a hex value to its corresponding hex character
148 class function HexChar( val : Byte) : Byte;
149
150 // Write the bytes in array buf as a JSON characters, escaping as needed
151 procedure WriteJSONString( const b : TBytes); overload;
Roger Meier333bbf32012-01-08 21:51:08 +0000152 procedure WriteJSONString( const str : string); overload;
Jake Farrell27274222011-11-10 20:32:44 +0000153
154 // Write out number as a JSON value. If the context dictates so, it will be
155 // wrapped in quotes to output as a JSON string.
Roger Meier333bbf32012-01-08 21:51:08 +0000156 procedure WriteJSONInteger( const num : Int64);
Jake Farrell27274222011-11-10 20:32:44 +0000157
158 // Write out a double as a JSON value. If it is NaN or infinity or if the
159 // context dictates escaping, Write out as JSON string.
160 procedure WriteJSONDouble( const num : Double);
161
162 // Write out contents of byte array b as a JSON string with base-64 encoded data
163 procedure WriteJSONBase64( const b : TBytes);
164
165 procedure WriteJSONObjectStart;
166 procedure WriteJSONObjectEnd;
167 procedure WriteJSONArrayStart;
168 procedure WriteJSONArrayEnd;
169
170 public
171 // IProtocol
Roger Meier333bbf32012-01-08 21:51:08 +0000172 procedure WriteMessageBegin( const aMsg : IMessage); override;
Jake Farrell27274222011-11-10 20:32:44 +0000173 procedure WriteMessageEnd; override;
Roger Meier333bbf32012-01-08 21:51:08 +0000174 procedure WriteStructBegin( const struc: IStruct); override;
Jake Farrell27274222011-11-10 20:32:44 +0000175 procedure WriteStructEnd; override;
Roger Meier333bbf32012-01-08 21:51:08 +0000176 procedure WriteFieldBegin( const field: IField); override;
Jake Farrell27274222011-11-10 20:32:44 +0000177 procedure WriteFieldEnd; override;
178 procedure WriteFieldStop; override;
Roger Meier333bbf32012-01-08 21:51:08 +0000179 procedure WriteMapBegin( const map: IMap); override;
Jake Farrell27274222011-11-10 20:32:44 +0000180 procedure WriteMapEnd; override;
Roger Meier333bbf32012-01-08 21:51:08 +0000181 procedure WriteListBegin( const list: IList); override;
Jake Farrell27274222011-11-10 20:32:44 +0000182 procedure WriteListEnd(); override;
Roger Meier333bbf32012-01-08 21:51:08 +0000183 procedure WriteSetBegin( const set_: ISet ); override;
Jake Farrell27274222011-11-10 20:32:44 +0000184 procedure WriteSetEnd(); override;
185 procedure WriteBool( b: Boolean); override;
186 procedure WriteByte( b: ShortInt); override;
187 procedure WriteI16( i16: SmallInt); override;
188 procedure WriteI32( i32: Integer); override;
Roger Meier333bbf32012-01-08 21:51:08 +0000189 procedure WriteI64( const i64: Int64); override;
190 procedure WriteDouble( const d: Double); override;
Jake Farrell27274222011-11-10 20:32:44 +0000191 procedure WriteString( const s: string ); override;
192 procedure WriteBinary( const b: TBytes); override;
193 //
194 function ReadMessageBegin: IMessage; override;
195 procedure ReadMessageEnd(); override;
196 function ReadStructBegin: IStruct; override;
197 procedure ReadStructEnd; override;
198 function ReadFieldBegin: IField; override;
199 procedure ReadFieldEnd(); override;
200 function ReadMapBegin: IMap; override;
201 procedure ReadMapEnd(); override;
202 function ReadListBegin: IList; override;
203 procedure ReadListEnd(); override;
204 function ReadSetBegin: ISet; override;
205 procedure ReadSetEnd(); override;
206 function ReadBool: Boolean; override;
207 function ReadByte: ShortInt; override;
208 function ReadI16: SmallInt; override;
209 function ReadI32: Integer; override;
210 function ReadI64: Int64; override;
211 function ReadDouble:Double; override;
212 function ReadString : string; override;
213 function ReadBinary: TBytes; override;
214
215
216 private
217 // Reading methods.
218
219 // Read in a JSON string, unescaping as appropriate.
220 // Skip Reading from the context if skipContext is true.
221 function ReadJSONString( skipContext : Boolean) : TBytes;
222
223 // Return true if the given byte could be a valid part of a JSON number.
224 function IsJSONNumeric( b : Byte) : Boolean;
225
226 // Read in a sequence of characters that are all valid in JSON numbers. Does
227 // not do a complete regex check to validate that this is actually a number.
228 function ReadJSONNumericChars : String;
229
230 // Read in a JSON number. If the context dictates, Read in enclosing quotes.
231 function ReadJSONInteger : Int64;
232
233 // Read in a JSON double value. Throw if the value is not wrapped in quotes
234 // when expected or if wrapped in quotes when not expected.
235 function ReadJSONDouble : Double;
236
237 // Read in a JSON string containing base-64 encoded data and decode it.
238 function ReadJSONBase64 : TBytes;
239
240 procedure ReadJSONObjectStart;
241 procedure ReadJSONObjectEnd;
242 procedure ReadJSONArrayStart;
243 procedure ReadJSONArrayEnd;
244 end;
245
246
247implementation
248
249var
250 COMMA : TBytes;
251 COLON : TBytes;
252 LBRACE : TBytes;
253 RBRACE : TBytes;
254 LBRACKET : TBytes;
255 RBRACKET : TBytes;
256 QUOTE : TBytes;
257 BACKSLASH : TBytes;
Jake Farrell27274222011-11-10 20:32:44 +0000258 ESCSEQ : TBytes;
259
260const
261 VERSION = 1;
262 JSON_CHAR_TABLE : array[0..$2F] of Byte
263 = (0,0,0,0, 0,0,0,0, Byte('b'),Byte('t'),Byte('n'),0, Byte('f'),Byte('r'),0,0,
264 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
265 1,1,Byte('"'),1, 1,1,1,1, 1,1,1,1, 1,1,1,1);
266
Jens Geyer21366942013-12-30 22:04:51 +0100267 ESCAPE_CHARS = '"\/btnfr';
268 ESCAPE_CHAR_VALS = '"\/'#8#9#10#12#13;
Jake Farrell27274222011-11-10 20:32:44 +0000269
270 DEF_STRING_SIZE = 16;
271
272 NAME_BOOL = 'tf';
273 NAME_BYTE = 'i8';
274 NAME_I16 = 'i16';
275 NAME_I32 = 'i32';
276 NAME_I64 = 'i64';
277 NAME_DOUBLE = 'dbl';
278 NAME_STRUCT = 'rec';
279 NAME_STRING = 'str';
280 NAME_MAP = 'map';
281 NAME_LIST = 'lst';
282 NAME_SET = 'set';
283
284 INVARIANT_CULTURE : TFormatSettings
285 = ( ThousandSeparator: ',';
286 DecimalSeparator: '.');
287
288
289
290//--- TJSONProtocolImpl ----------------------
291
292
Roger Meier333bbf32012-01-08 21:51:08 +0000293function TJSONProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
Jake Farrell27274222011-11-10 20:32:44 +0000294begin
295 result := TJSONProtocolImpl.Create(trans);
296end;
297
298class function TJSONProtocolImpl.GetTypeNameForTypeID(typeID : TType) : string;
299begin
300 case typeID of
301 TType.Bool_: result := NAME_BOOL;
302 TType.Byte_: result := NAME_BYTE;
303 TType.I16: result := NAME_I16;
304 TType.I32: result := NAME_I32;
305 TType.I64: result := NAME_I64;
306 TType.Double_: result := NAME_DOUBLE;
307 TType.String_: result := NAME_STRING;
308 TType.Struct: result := NAME_STRUCT;
309 TType.Map: result := NAME_MAP;
310 TType.Set_: result := NAME_SET;
311 TType.List: result := NAME_LIST;
312 else
313 raise TProtocolException.Create( TProtocolException.NOT_IMPLEMENTED, 'Unrecognized type ('+IntToStr(Ord(typeID))+')');
314 end;
315end;
316
317
318class function TJSONProtocolImpl.GetTypeIDForTypeName( const name : string) : TType;
319begin
320 if name = NAME_BOOL then result := TType.Bool_
321 else if name = NAME_BYTE then result := TType.Byte_
322 else if name = NAME_I16 then result := TType.I16
323 else if name = NAME_I32 then result := TType.I32
324 else if name = NAME_I64 then result := TType.I64
325 else if name = NAME_DOUBLE then result := TType.Double_
326 else if name = NAME_STRUCT then result := TType.Struct
327 else if name = NAME_STRING then result := TType.String_
328 else if name = NAME_MAP then result := TType.Map
329 else if name = NAME_LIST then result := TType.List
330 else if name = NAME_SET then result := TType.Set_
331 else raise TProtocolException.Create( TProtocolException.NOT_IMPLEMENTED, 'Unrecognized type ('+name+')');
332end;
333
334
335constructor TJSONProtocolImpl.TJSONBaseContext.Create( const aProto : IJSONProtocol);
336begin
337 inherited Create;
Roger Meierfebe8452012-06-06 10:32:24 +0000338 FProto := Pointer(aProto);
Jake Farrell27274222011-11-10 20:32:44 +0000339end;
340
341
342procedure TJSONProtocolImpl.TJSONBaseContext.Write;
343begin
344 // nothing
345end;
346
347
348procedure TJSONProtocolImpl.TJSONBaseContext.Read;
349begin
350 // nothing
351end;
352
353
354function TJSONProtocolImpl.TJSONBaseContext.EscapeNumbers : Boolean;
355begin
356 result := FALSE;
357end;
358
359
360constructor TJSONProtocolImpl.TJSONListContext.Create( const aProto : IJSONProtocol);
361begin
362 inherited Create( aProto);
363 FFirst := TRUE;
364end;
365
366
367procedure TJSONProtocolImpl.TJSONListContext.Write;
368begin
369 if FFirst
370 then FFirst := FALSE
Roger Meierfebe8452012-06-06 10:32:24 +0000371 else IJSONProtocol(FProto).Transport.Write( COMMA);
Jake Farrell27274222011-11-10 20:32:44 +0000372end;
373
374
375procedure TJSONProtocolImpl.TJSONListContext.Read;
376begin
377 if FFirst
378 then FFirst := FALSE
Roger Meierfebe8452012-06-06 10:32:24 +0000379 else IJSONProtocol(FProto).ReadJSONSyntaxChar( COMMA[0]);
Jake Farrell27274222011-11-10 20:32:44 +0000380end;
381
382
383constructor TJSONProtocolImpl.TJSONPairContext.Create( const aProto : IJSONProtocol);
384begin
385 inherited Create( aProto);
386 FFirst := TRUE;
387 FColon := TRUE;
388end;
389
390
391procedure TJSONProtocolImpl.TJSONPairContext.Write;
392begin
393 if FFirst then begin
394 FFirst := FALSE;
395 FColon := TRUE;
396 end
397 else begin
398 if FColon
Roger Meierfebe8452012-06-06 10:32:24 +0000399 then IJSONProtocol(FProto).Transport.Write( COLON)
400 else IJSONProtocol(FProto).Transport.Write( COMMA);
Jake Farrell27274222011-11-10 20:32:44 +0000401 FColon := not FColon;
402 end;
403end;
404
405
406procedure TJSONProtocolImpl.TJSONPairContext.Read;
407begin
408 if FFirst then begin
409 FFirst := FALSE;
410 FColon := TRUE;
411 end
412 else begin
413 if FColon
Roger Meierfebe8452012-06-06 10:32:24 +0000414 then IJSONProtocol(FProto).ReadJSONSyntaxChar( COLON[0])
415 else IJSONProtocol(FProto).ReadJSONSyntaxChar( COMMA[0]);
Jake Farrell27274222011-11-10 20:32:44 +0000416 FColon := not FColon;
417 end;
418end;
419
420
421function TJSONProtocolImpl.TJSONPairContext.EscapeNumbers : Boolean;
422begin
423 result := FColon;
424end;
425
426
427constructor TJSONProtocolImpl.TLookaheadReader.Create( const aProto : IJSONProtocol);
428begin
429 inherited Create;
Roger Meierfebe8452012-06-06 10:32:24 +0000430 FProto := Pointer(aProto);
Jake Farrell27274222011-11-10 20:32:44 +0000431 FHasData := FALSE;
432end;
433
434
435function TJSONProtocolImpl.TLookaheadReader.Read : Byte;
436begin
437 if FHasData
438 then FHasData := FALSE
439 else begin
440 SetLength( FData, 1);
Roger Meierfebe8452012-06-06 10:32:24 +0000441 IJSONProtocol(FProto).Transport.ReadAll( FData, 0, 1);
Jake Farrell27274222011-11-10 20:32:44 +0000442 end;
443 result := FData[0];
444end;
445
446
447function TJSONProtocolImpl.TLookaheadReader.Peek : Byte;
448begin
449 if not FHasData then begin
450 SetLength( FData, 1);
Roger Meierfebe8452012-06-06 10:32:24 +0000451 IJSONProtocol(FProto).Transport.ReadAll( FData, 0, 1);
Jake Farrell27274222011-11-10 20:32:44 +0000452 FHasData := TRUE;
453 end;
454 result := FData[0];
455end;
456
457
Roger Meier333bbf32012-01-08 21:51:08 +0000458constructor TJSONProtocolImpl.Create( const aTrans : ITransport);
Jake Farrell27274222011-11-10 20:32:44 +0000459begin
460 inherited Create( aTrans);
461
462 // Stack of nested contexts that we may be in
463 FContextStack := TStack<TJSONBaseContext>.Create;
464
465 FContext := TJSONBaseContext.Create( Self);
466 FReader := TLookaheadReader.Create( Self);
467end;
468
469
470destructor TJSONProtocolImpl.Destroy;
471begin
472 try
Roger Meier45a37262012-01-08 21:44:44 +0000473 ResetContextStack; // free any contents
Jake Farrell27274222011-11-10 20:32:44 +0000474 FreeAndNil( FReader);
475 FreeAndNil( FContext);
476 FreeAndNil( FContextStack);
477 finally
478 inherited Destroy;
479 end;
480end;
481
482
Roger Meier45a37262012-01-08 21:44:44 +0000483procedure TJSONProtocolImpl.ResetContextStack;
484begin
485 while FContextStack.Count > 0
486 do PopContext;
487end;
488
489
Roger Meier333bbf32012-01-08 21:51:08 +0000490procedure TJSONProtocolImpl.PushContext( const aCtx : TJSONBaseContext);
Roger Meier45a37262012-01-08 21:44:44 +0000491begin
492 FContextStack.Push( FContext);
493 FContext := aCtx;
494end;
495
496
497procedure TJSONProtocolImpl.PopContext;
498begin
499 FreeAndNil(FContext);
500 FContext := FContextStack.Pop;
501end;
502
503
Jake Farrell27274222011-11-10 20:32:44 +0000504procedure TJSONProtocolImpl.ReadJSONSyntaxChar( b : Byte);
505var ch : Byte;
506begin
507 ch := FReader.Read;
508 if (ch <> b)
509 then raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Unexpected character ('+Char(ch)+')');
510end;
511
512
513class function TJSONProtocolImpl.HexVal( ch : Byte) : Byte;
514var i : Integer;
515begin
516 i := StrToIntDef( '$0'+Char(ch), -1);
517 if (0 <= i) and (i < $10)
518 then result := i
519 else raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Expected hex character ('+Char(ch)+')');
520end;
521
522
523class function TJSONProtocolImpl.HexChar( val : Byte) : Byte;
524const HEXCHARS = '0123456789ABCDEF';
525begin
526 result := Byte( PChar(HEXCHARS)[val and $0F]);
527 ASSERT( Pos( Char(result), HEXCHARS) > 0);
528end;
529
530
Roger Meier333bbf32012-01-08 21:51:08 +0000531procedure TJSONProtocolImpl.WriteJSONString( const str : string);
Jake Farrell27274222011-11-10 20:32:44 +0000532begin
533 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( str));
534end;
535
536
537procedure TJSONProtocolImpl.WriteJSONString( const b : TBytes);
538var i : Integer;
539 tmp : TBytes;
540begin
541 FContext.Write;
542 Transport.Write( QUOTE);
543 for i := 0 to Length(b)-1 do begin
544
545 if (b[i] and $00FF) >= $30 then begin
546
547 if (b[i] = BACKSLASH[0]) then begin
548 Transport.Write( BACKSLASH);
549 Transport.Write( BACKSLASH);
550 end
551 else begin
552 Transport.Write( b, i, 1);
553 end;
554
555 end
556 else begin
557 SetLength( tmp, 2);
558 tmp[0] := JSON_CHAR_TABLE[b[i]];
559 if (tmp[0] = 1) then begin
560 Transport.Write( b, i, 1)
561 end
562 else if (tmp[0] > 1) then begin
563 Transport.Write( BACKSLASH);
564 Transport.Write( tmp, 0, 1);
565 end
566 else begin
567 Transport.Write( ESCSEQ);
568 tmp[0] := HexChar( b[i] div $10);
569 tmp[1] := HexChar( b[i]);
570 Transport.Write( tmp, 0, 2);
571 end;
572 end;
573 end;
574 Transport.Write( QUOTE);
575end;
576
577
Roger Meier333bbf32012-01-08 21:51:08 +0000578procedure TJSONProtocolImpl.WriteJSONInteger( const num : Int64);
Jake Farrell27274222011-11-10 20:32:44 +0000579var str : String;
580 escapeNum : Boolean;
581begin
582 FContext.Write;
583 str := IntToStr(num);
584
585 escapeNum := FContext.EscapeNumbers;
586 if escapeNum
587 then Transport.Write( QUOTE);
588
589 Transport.Write( SysUtils.TEncoding.UTF8.GetBytes( str));
590
591 if escapeNum
592 then Transport.Write( QUOTE);
593end;
594
595
596procedure TJSONProtocolImpl.WriteJSONDouble( const num : Double);
597var str : string;
598 special : Boolean;
599 escapeNum : Boolean;
600begin
601 FContext.Write;
602
603 str := FloatToStr( num, INVARIANT_CULTURE);
604 special := FALSE;
605
606 case UpCase(str[1]) of
607 'N' : special := TRUE; // NaN
608 'I' : special := TRUE; // Infinity
609 '-' : special := (UpCase(str[2]) = 'I'); // -Infinity
610 end;
611
612 escapeNum := special or FContext.EscapeNumbers;
613
614
615 if escapeNum
616 then Transport.Write( QUOTE);
617
618 Transport.Write( SysUtils.TEncoding.UTF8.GetBytes( str));
619
620 if escapeNum
621 then Transport.Write( QUOTE);
622end;
623
624
625procedure TJSONProtocolImpl.WriteJSONBase64( const b : TBytes);
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100626var len, off, cnt : Integer;
627 tmpBuf : TBytes;
Jake Farrell27274222011-11-10 20:32:44 +0000628begin
629 FContext.Write;
630 Transport.Write( QUOTE);
631
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100632 len := Length(b);
633 off := 0;
634 SetLength( tmpBuf, 4);
635
636 while len >= 3 do begin
637 // Encode 3 bytes at a time
638 Base64Utils.Encode( b, off, 3, tmpBuf, 0);
639 Transport.Write( tmpBuf, 0, 4);
640 Inc( off, 3);
641 Dec( len, 3);
Jake Farrell27274222011-11-10 20:32:44 +0000642 end;
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100643
644 // Encode remainder, if any
645 if len > 0 then begin
646 cnt := Base64Utils.Encode( b, off, len, tmpBuf, 0);
647 Transport.Write( tmpBuf, 0, cnt);
648 end;
Jake Farrell27274222011-11-10 20:32:44 +0000649
650 Transport.Write( QUOTE);
651end;
652
653
654procedure TJSONProtocolImpl.WriteJSONObjectStart;
655begin
656 FContext.Write;
657 Transport.Write( LBRACE);
658 PushContext( TJSONPairContext.Create( Self));
659end;
660
661
662procedure TJSONProtocolImpl.WriteJSONObjectEnd;
663begin
664 PopContext;
665 Transport.Write( RBRACE);
666end;
667
668
669procedure TJSONProtocolImpl.WriteJSONArrayStart;
670begin
671 FContext.Write;
672 Transport.Write( LBRACKET);
673 PushContext( TJSONListContext.Create( Self));
674end;
675
676
677procedure TJSONProtocolImpl.WriteJSONArrayEnd;
678begin
679 PopContext;
680 Transport.Write( RBRACKET);
681end;
682
683
Roger Meier333bbf32012-01-08 21:51:08 +0000684procedure TJSONProtocolImpl.WriteMessageBegin( const aMsg : IMessage);
Jake Farrell27274222011-11-10 20:32:44 +0000685begin
Roger Meier45a37262012-01-08 21:44:44 +0000686 ResetContextStack; // THRIFT-1473
687
Jake Farrell27274222011-11-10 20:32:44 +0000688 WriteJSONArrayStart;
689 WriteJSONInteger(VERSION);
690
691 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( aMsg.Name));
692
693 WriteJSONInteger( LongInt( aMsg.Type_));
694 WriteJSONInteger( aMsg.SeqID);
695end;
696
697procedure TJSONProtocolImpl.WriteMessageEnd;
698begin
699 WriteJSONArrayEnd;
700end;
701
702
Roger Meier333bbf32012-01-08 21:51:08 +0000703procedure TJSONProtocolImpl.WriteStructBegin( const struc: IStruct);
Jake Farrell27274222011-11-10 20:32:44 +0000704begin
705 WriteJSONObjectStart;
706end;
707
708
709procedure TJSONProtocolImpl.WriteStructEnd;
710begin
711 WriteJSONObjectEnd;
712end;
713
714
Roger Meier333bbf32012-01-08 21:51:08 +0000715procedure TJSONProtocolImpl.WriteFieldBegin( const field : IField);
Jake Farrell27274222011-11-10 20:32:44 +0000716begin
717 WriteJSONInteger(field.ID);
718 WriteJSONObjectStart;
719 WriteJSONString( GetTypeNameForTypeID(field.Type_));
720end;
721
722
723procedure TJSONProtocolImpl.WriteFieldEnd;
724begin
725 WriteJSONObjectEnd;
726end;
727
728
729procedure TJSONProtocolImpl.WriteFieldStop;
730begin
731 // nothing to do
732end;
733
Roger Meier333bbf32012-01-08 21:51:08 +0000734procedure TJSONProtocolImpl.WriteMapBegin( const map: IMap);
Jake Farrell27274222011-11-10 20:32:44 +0000735begin
736 WriteJSONArrayStart;
737 WriteJSONString( GetTypeNameForTypeID( map.KeyType));
738 WriteJSONString( GetTypeNameForTypeID( map.ValueType));
739 WriteJSONInteger( map.Count);
740 WriteJSONObjectStart;
741end;
742
743
744procedure TJSONProtocolImpl.WriteMapEnd;
745begin
746 WriteJSONObjectEnd;
747 WriteJSONArrayEnd;
748end;
749
750
Roger Meier333bbf32012-01-08 21:51:08 +0000751procedure TJSONProtocolImpl.WriteListBegin( const list: IList);
Jake Farrell27274222011-11-10 20:32:44 +0000752begin
753 WriteJSONArrayStart;
754 WriteJSONString( GetTypeNameForTypeID( list.ElementType));
755 WriteJSONInteger(list.Count);
756end;
757
758
759procedure TJSONProtocolImpl.WriteListEnd;
760begin
761 WriteJSONArrayEnd;
762end;
763
764
Roger Meier333bbf32012-01-08 21:51:08 +0000765procedure TJSONProtocolImpl.WriteSetBegin( const set_: ISet);
Jake Farrell27274222011-11-10 20:32:44 +0000766begin
767 WriteJSONArrayStart;
768 WriteJSONString( GetTypeNameForTypeID( set_.ElementType));
769 WriteJSONInteger( set_.Count);
770end;
771
772
773procedure TJSONProtocolImpl.WriteSetEnd;
774begin
775 WriteJSONArrayEnd;
776end;
777
778procedure TJSONProtocolImpl.WriteBool( b: Boolean);
779begin
780 if b
781 then WriteJSONInteger( 1)
782 else WriteJSONInteger( 0);
783end;
784
785procedure TJSONProtocolImpl.WriteByte( b: ShortInt);
786begin
787 WriteJSONInteger( b);
788end;
789
790procedure TJSONProtocolImpl.WriteI16( i16: SmallInt);
791begin
792 WriteJSONInteger( i16);
793end;
794
795procedure TJSONProtocolImpl.WriteI32( i32: Integer);
796begin
797 WriteJSONInteger( i32);
798end;
799
Roger Meier333bbf32012-01-08 21:51:08 +0000800procedure TJSONProtocolImpl.WriteI64( const i64: Int64);
Jake Farrell27274222011-11-10 20:32:44 +0000801begin
802 WriteJSONInteger(i64);
803end;
804
Roger Meier333bbf32012-01-08 21:51:08 +0000805procedure TJSONProtocolImpl.WriteDouble( const d: Double);
Jake Farrell27274222011-11-10 20:32:44 +0000806begin
807 WriteJSONDouble( d);
808end;
809
810procedure TJSONProtocolImpl.WriteString( const s: string );
811begin
812 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( s));
813end;
814
815procedure TJSONProtocolImpl.WriteBinary( const b: TBytes);
816begin
817 WriteJSONBase64( b);
818end;
819
820
821function TJSONProtocolImpl.ReadJSONString( skipContext : Boolean) : TBytes;
822var buffer : TMemoryStream;
Jens Geyer7bb44a32014-02-07 22:24:37 +0100823 ch : Byte;
824 wch : Word;
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700825 highSurogate: Char;
826 surrogatePairs: Array[0..1] of Char;
Jake Farrell27274222011-11-10 20:32:44 +0000827 off : Integer;
828 tmp : TBytes;
829begin
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700830 highSurogate := #0;
Jake Farrell27274222011-11-10 20:32:44 +0000831 buffer := TMemoryStream.Create;
832 try
833 if not skipContext
834 then FContext.Read;
835
836 ReadJSONSyntaxChar( QUOTE[0]);
837
838 while TRUE do begin
839 ch := FReader.Read;
840
841 if (ch = QUOTE[0])
842 then Break;
843
Jens Geyer7bb44a32014-02-07 22:24:37 +0100844 // check for escapes
845 if (ch <> ESCSEQ[0]) then begin
846 buffer.Write( ch, 1);
847 Continue;
Jake Farrell27274222011-11-10 20:32:44 +0000848 end;
Jens Geyer7bb44a32014-02-07 22:24:37 +0100849
850 // distuinguish between \uNNNN and \?
851 ch := FReader.Read;
852 if (ch <> ESCSEQ[1])
853 then begin
854 off := Pos( Char(ch), ESCAPE_CHARS);
855 if off < 1
856 then raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Expected control char');
857 ch := Byte( ESCAPE_CHAR_VALS[off]);
858 buffer.Write( ch, 1);
859 Continue;
860 end;
861
862 // it is \uXXXX
863 SetLength( tmp, 4);
864 Transport.ReadAll( tmp, 0, 4);
865 wch := (HexVal(tmp[0]) shl 12)
866 + (HexVal(tmp[1]) shl 8)
867 + (HexVal(tmp[2]) shl 4)
868 + HexVal(tmp[3]);
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700869
Jens Geyer7bb44a32014-02-07 22:24:37 +0100870 // we need to make UTF8 bytes from it, to be decoded later
Jens Geyer71070432016-01-29 10:08:39 +0100871 if CharUtils.IsHighSurrogate(char(wch)) then begin
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700872 if highSurogate <> #0
873 then raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Expected low surrogate char');
874 highSurogate := char(wch);
875 end
Jens Geyer71070432016-01-29 10:08:39 +0100876 else if CharUtils.IsLowSurrogate(char(wch)) then begin
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700877 if highSurogate = #0
878 then TProtocolException.Create( TProtocolException.INVALID_DATA, 'Expected high surrogate char');
879 surrogatePairs[0] := highSurogate;
880 surrogatePairs[1] := char(wch);
881 tmp := TEncoding.UTF8.GetBytes(surrogatePairs);
882 buffer.Write( tmp[0], Length(tmp));
883 highSurogate := #0;
884 end
885 else begin
886 tmp := SysUtils.TEncoding.UTF8.GetBytes(Char(wch));
887 buffer.Write( tmp[0], Length(tmp));
888 end;
Jake Farrell27274222011-11-10 20:32:44 +0000889 end;
890
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700891 if highSurogate <> #0
892 then raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Expected low surrogate char');
893
Jake Farrell27274222011-11-10 20:32:44 +0000894 SetLength( result, buffer.Size);
Jake Farrella2a9ee92011-12-15 20:50:31 +0000895 if buffer.Size > 0 then Move( buffer.Memory^, result[0], Length(result));
Jake Farrell27274222011-11-10 20:32:44 +0000896
897 finally
898 buffer.Free;
899 end;
900end;
901
902
903function TJSONProtocolImpl.IsJSONNumeric( b : Byte) : Boolean;
904const NUMCHARS = ['+','-','.','0','1','2','3','4','5','6','7','8','9','E','e'];
905begin
906 result := CharInSet( Char(b), NUMCHARS);
907end;
908
909
910function TJSONProtocolImpl.ReadJSONNumericChars : string;
911var strbld : TThriftStringBuilder;
912 ch : Byte;
913begin
914 strbld := TThriftStringBuilder.Create;
915 try
916 while TRUE do begin
917 ch := FReader.Peek;
918 if IsJSONNumeric(ch)
919 then strbld.Append( Char(FReader.Read))
920 else Break;
921 end;
922 result := strbld.ToString;
923
924 finally
925 strbld.Free;
926 end;
927end;
928
929
930function TJSONProtocolImpl.ReadJSONInteger : Int64;
931var str : string;
932begin
933 FContext.Read;
934 if FContext.EscapeNumbers
935 then ReadJSONSyntaxChar( QUOTE[0]);
936
937 str := ReadJSONNumericChars;
938
939 if FContext.EscapeNumbers
940 then ReadJSONSyntaxChar( QUOTE[0]);
941
942 try
943 result := StrToInt64(str);
944 except
945 on e:Exception do begin
946 raise TProtocolException.Create( TProtocolException.INVALID_DATA,
947 'Bad data encounted in numeric data ('+str+') ('+e.Message+')');
948 end;
949 end;
950end;
951
952
953function TJSONProtocolImpl.ReadJSONDouble : Double;
954var dub : Double;
955 str : string;
956begin
957 FContext.Read;
958
959 if FReader.Peek = QUOTE[0]
960 then begin
961 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( TRUE));
962 dub := StrToFloat( str, INVARIANT_CULTURE);
963
964 if not FContext.EscapeNumbers()
965 and not Math.IsNaN(dub)
966 and not Math.IsInfinite(dub)
967 then begin
968 // Throw exception -- we should not be in a string in Self case
969 raise TProtocolException.Create( TProtocolException.INVALID_DATA, 'Numeric data unexpectedly quoted');
970 end;
971 result := dub;
972 Exit;
973 end;
974
975 // will throw - we should have had a quote if escapeNum == true
976 if FContext.EscapeNumbers
977 then ReadJSONSyntaxChar( QUOTE[0]);
978
979 try
980 str := ReadJSONNumericChars;
981 result := StrToFloat( str, INVARIANT_CULTURE);
982 except
983 on e:Exception
984 do raise TProtocolException.Create( TProtocolException.INVALID_DATA,
985 'Bad data encounted in numeric data ('+str+') ('+e.Message+')');
986 end;
987end;
988
989
990function TJSONProtocolImpl.ReadJSONBase64 : TBytes;
991var b : TBytes;
Jens Geyer9f9535c2014-12-14 04:16:05 +0100992 len, off, size : Integer;
Jake Farrell27274222011-11-10 20:32:44 +0000993begin
994 b := ReadJSONString(false);
995
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100996 len := Length(b);
997 off := 0;
998 size := 0;
999
1000 // reduce len to ignore fill bytes
1001 Dec(len);
1002 while (len >= 0) and (b[len] = Byte('=')) do Dec(len);
1003 Inc(len);
1004
1005 // read & decode full byte triplets = 4 source bytes
1006 while (len >= 4) do begin
1007 // Decode 4 bytes at a time
1008 Inc( size, Base64Utils.Decode( b, off, 4, b, size)); // decoded in place
1009 Inc( off, 4);
1010 Dec( len, 4);
1011 end;
1012
1013 // Don't decode if we hit the end or got a single leftover byte (invalid
1014 // base64 but legal for skip of regular string type)
1015 if len > 1 then begin
1016 // Decode remainder
1017 Inc( size, Base64Utils.Decode( b, off, len, b, size)); // decoded in place
1018 end;
1019
1020 // resize to final size and return the data
1021 SetLength( b, size);
1022 result := b;
Jake Farrell27274222011-11-10 20:32:44 +00001023end;
1024
1025
1026procedure TJSONProtocolImpl.ReadJSONObjectStart;
1027begin
1028 FContext.Read;
1029 ReadJSONSyntaxChar( LBRACE[0]);
1030 PushContext( TJSONPairContext.Create( Self));
1031end;
1032
1033
1034procedure TJSONProtocolImpl.ReadJSONObjectEnd;
1035begin
1036 ReadJSONSyntaxChar( RBRACE[0]);
1037 PopContext;
1038end;
1039
1040
1041procedure TJSONProtocolImpl.ReadJSONArrayStart;
1042begin
1043 FContext.Read;
1044 ReadJSONSyntaxChar( LBRACKET[0]);
1045 PushContext( TJSONListContext.Create( Self));
1046end;
1047
1048
1049procedure TJSONProtocolImpl.ReadJSONArrayEnd;
1050begin
1051 ReadJSONSyntaxChar( RBRACKET[0]);
1052 PopContext;
1053end;
1054
1055
1056function TJSONProtocolImpl.ReadMessageBegin: IMessage;
1057begin
Roger Meier45a37262012-01-08 21:44:44 +00001058 ResetContextStack; // THRIFT-1473
1059
Jake Farrell27274222011-11-10 20:32:44 +00001060 result := TMessageImpl.Create;
1061 ReadJSONArrayStart;
1062
1063 if ReadJSONInteger <> VERSION
1064 then raise TProtocolException.Create( TProtocolException.BAD_VERSION, 'Message contained bad version.');
1065
1066 result.Name := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
1067 result.Type_ := TMessageType( ReadJSONInteger);
1068 result.SeqID := ReadJSONInteger;
1069end;
1070
1071
1072procedure TJSONProtocolImpl.ReadMessageEnd;
1073begin
1074 ReadJSONArrayEnd;
1075end;
1076
1077
1078function TJSONProtocolImpl.ReadStructBegin : IStruct ;
1079begin
1080 ReadJSONObjectStart;
1081 result := TStructImpl.Create('');
1082end;
1083
1084
1085procedure TJSONProtocolImpl.ReadStructEnd;
1086begin
1087 ReadJSONObjectEnd;
1088end;
1089
1090
1091function TJSONProtocolImpl.ReadFieldBegin : IField;
1092var ch : Byte;
1093 str : string;
1094begin
1095 result := TFieldImpl.Create;
1096 ch := FReader.Peek;
1097 if ch = RBRACE[0]
1098 then result.Type_ := TType.Stop
1099 else begin
1100 result.ID := ReadJSONInteger;
1101 ReadJSONObjectStart;
1102
1103 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
1104 result.Type_ := GetTypeIDForTypeName( str);
1105 end;
1106end;
1107
1108
1109procedure TJSONProtocolImpl.ReadFieldEnd;
1110begin
1111 ReadJSONObjectEnd;
1112end;
1113
1114
1115function TJSONProtocolImpl.ReadMapBegin : IMap;
1116var str : string;
1117begin
1118 result := TMapImpl.Create;
1119 ReadJSONArrayStart;
1120
1121 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1122 result.KeyType := GetTypeIDForTypeName( str);
1123
1124 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1125 result.ValueType := GetTypeIDForTypeName( str);
1126
1127 result.Count := ReadJSONInteger;
1128 ReadJSONObjectStart;
1129end;
1130
1131
1132procedure TJSONProtocolImpl.ReadMapEnd;
1133begin
1134 ReadJSONObjectEnd;
1135 ReadJSONArrayEnd;
1136end;
1137
1138
1139function TJSONProtocolImpl.ReadListBegin : IList;
1140var str : string;
1141begin
1142 result := TListImpl.Create;
1143 ReadJSONArrayStart;
1144
1145 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1146 result.ElementType := GetTypeIDForTypeName( str);
1147 result.Count := ReadJSONInteger;
1148end;
1149
1150
1151procedure TJSONProtocolImpl.ReadListEnd;
1152begin
1153 ReadJSONArrayEnd;
1154end;
1155
1156
1157function TJSONProtocolImpl.ReadSetBegin : ISet;
1158var str : string;
1159begin
1160 result := TSetImpl.Create;
1161 ReadJSONArrayStart;
1162
1163 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1164 result.ElementType := GetTypeIDForTypeName( str);
1165 result.Count := ReadJSONInteger;
1166end;
1167
1168
1169procedure TJSONProtocolImpl.ReadSetEnd;
1170begin
1171 ReadJSONArrayEnd;
1172end;
1173
1174
1175function TJSONProtocolImpl.ReadBool : Boolean;
1176begin
1177 result := (ReadJSONInteger <> 0);
1178end;
1179
1180
1181function TJSONProtocolImpl.ReadByte : ShortInt;
1182begin
1183 result := ReadJSONInteger;
1184end;
1185
1186
1187function TJSONProtocolImpl.ReadI16 : SmallInt;
1188begin
1189 result := ReadJSONInteger;
1190end;
1191
1192
1193function TJSONProtocolImpl.ReadI32 : LongInt;
1194begin
1195 result := ReadJSONInteger;
1196end;
1197
1198
1199function TJSONProtocolImpl.ReadI64 : Int64;
1200begin
1201 result := ReadJSONInteger;
1202end;
1203
1204
1205function TJSONProtocolImpl.ReadDouble : Double;
1206begin
1207 result := ReadJSONDouble;
1208end;
1209
1210
1211function TJSONProtocolImpl.ReadString : string;
1212begin
1213 result := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
1214end;
1215
1216
1217function TJSONProtocolImpl.ReadBinary : TBytes;
1218begin
1219 result := ReadJSONBase64;
1220end;
1221
1222
1223//--- init code ---
1224
1225procedure InitBytes( var b : TBytes; aData : array of Byte);
1226begin
1227 SetLength( b, Length(aData));
1228 Move( aData, b[0], Length(b));
1229end;
1230
1231initialization
1232 InitBytes( COMMA, [Byte(',')]);
1233 InitBytes( COLON, [Byte(':')]);
1234 InitBytes( LBRACE, [Byte('{')]);
1235 InitBytes( RBRACE, [Byte('}')]);
1236 InitBytes( LBRACKET, [Byte('[')]);
1237 InitBytes( RBRACKET, [Byte(']')]);
1238 InitBytes( QUOTE, [Byte('"')]);
1239 InitBytes( BACKSLASH, [Byte('\')]);
Jake Farrell27274222011-11-10 20:32:44 +00001240 InitBytes( ESCSEQ, [Byte('\'),Byte('u'),Byte('0'),Byte('0')]);
1241end.