blob: 2a9682ced11984915ffed74c49e398843f95b78d [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,
Jens Geyera019cda2019-11-09 23:24:52 +010032 Thrift.Configuration,
Jake Farrell27274222011-11-10 20:32:44 +000033 Thrift.Transport,
Jens Geyerd8bddbc2014-12-14 00:41:33 +010034 Thrift.Protocol,
Jens Geyerf726ae32021-06-04 11:17:26 +020035 Thrift.Stream,
Jens Geyerd8bddbc2014-12-14 00:41:33 +010036 Thrift.Utils;
Jake Farrell27274222011-11-10 20:32:44 +000037
38type
39 IJSONProtocol = interface( IProtocol)
40 ['{F0DAFDBD-692A-4B71-9736-F5D485A2178F}']
41 // Read a byte that must match b; otherwise an exception is thrown.
42 procedure ReadJSONSyntaxChar( b : Byte);
43 end;
44
45 // JSON protocol implementation for thrift.
46 // This is a full-featured protocol supporting Write and Read.
47 // Please see the C++ class header for a detailed description of the protocol's wire format.
48 // Adapted from the C# version.
49 TJSONProtocolImpl = class( TProtocolImpl, IJSONProtocol)
50 public
51 type
52 TFactory = class( TInterfacedObject, IProtocolFactory)
53 public
Roger Meier333bbf32012-01-08 21:51:08 +000054 function GetProtocol( const trans: ITransport): IProtocol;
Jake Farrell27274222011-11-10 20:32:44 +000055 end;
56
Jens Geyerfad7fd32019-11-09 23:24:52 +010057 strict private
Jake Farrell27274222011-11-10 20:32:44 +000058 class function GetTypeNameForTypeID(typeID : TType) : string;
59 class function GetTypeIDForTypeName( const name : string) : TType;
60
Jens Geyerfad7fd32019-11-09 23:24:52 +010061 strict protected
Jake Farrell27274222011-11-10 20:32:44 +000062 type
63 // Base class for tracking JSON contexts that may require
64 // inserting/Reading additional JSON syntax characters.
65 // This base context does nothing.
66 TJSONBaseContext = class
Jens Geyerfad7fd32019-11-09 23:24:52 +010067 strict protected
Roger Meierfebe8452012-06-06 10:32:24 +000068 FProto : Pointer; // weak IJSONProtocol;
Jake Farrell27274222011-11-10 20:32:44 +000069 public
70 constructor Create( const aProto : IJSONProtocol);
71 procedure Write; virtual;
72 procedure Read; virtual;
73 function EscapeNumbers : Boolean; virtual;
74 end;
75
76 // Context for JSON lists.
77 // Will insert/Read commas before each item except for the first one.
78 TJSONListContext = class( TJSONBaseContext)
Jens Geyerfad7fd32019-11-09 23:24:52 +010079 strict private
Jake Farrell27274222011-11-10 20:32:44 +000080 FFirst : Boolean;
81 public
82 constructor Create( const aProto : IJSONProtocol);
83 procedure Write; override;
84 procedure Read; override;
85 end;
86
87 // Context for JSON records. Will insert/Read colons before the value portion of each record
88 // pair, and commas before each key except the first. In addition, will indicate that numbers
89 // in the key position need to be escaped in quotes (since JSON keys must be strings).
90 TJSONPairContext = class( TJSONBaseContext)
Jens Geyerfad7fd32019-11-09 23:24:52 +010091 strict private
Jake Farrell27274222011-11-10 20:32:44 +000092 FFirst, FColon : Boolean;
93 public
94 constructor Create( const aProto : IJSONProtocol);
95 procedure Write; override;
96 procedure Read; override;
97 function EscapeNumbers : Boolean; override;
98 end;
99
100 // Holds up to one byte from the transport
101 TLookaheadReader = class
Jens Geyerfad7fd32019-11-09 23:24:52 +0100102 strict protected
Roger Meierfebe8452012-06-06 10:32:24 +0000103 FProto : Pointer; // weak IJSONProtocol;
Jens Geyerfad7fd32019-11-09 23:24:52 +0100104
105 protected
Jake Farrell27274222011-11-10 20:32:44 +0000106 constructor Create( const aProto : IJSONProtocol);
107
Jens Geyerfad7fd32019-11-09 23:24:52 +0100108 strict private
Jake Farrell27274222011-11-10 20:32:44 +0000109 FHasData : Boolean;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200110 FData : Byte;
Jake Farrell27274222011-11-10 20:32:44 +0000111
112 public
113 // Return and consume the next byte to be Read, either taking it from the
114 // data buffer if present or getting it from the transport otherwise.
115 function Read : Byte;
116
117 // Return the next byte to be Read without consuming, filling the data
118 // buffer if it has not been filled alReady.
119 function Peek : Byte;
120 end;
121
Jens Geyerfad7fd32019-11-09 23:24:52 +0100122 strict protected
Jake Farrell27274222011-11-10 20:32:44 +0000123 // Stack of nested contexts that we may be in
124 FContextStack : TStack<TJSONBaseContext>;
125
126 // Current context that we are in
127 FContext : TJSONBaseContext;
128
129 // Reader that manages a 1-byte buffer
130 FReader : TLookaheadReader;
131
132 // Push/pop a new JSON context onto/from the stack.
Roger Meier45a37262012-01-08 21:44:44 +0000133 procedure ResetContextStack;
Roger Meier333bbf32012-01-08 21:51:08 +0000134 procedure PushContext( const aCtx : TJSONBaseContext);
Jake Farrell27274222011-11-10 20:32:44 +0000135 procedure PopContext;
136
Jens Geyer41f47af2019-11-09 23:24:52 +0100137 strict protected
138 function GetMinSerializedSize( const aType : TType) : Integer; override;
139 procedure Reset; override;
140
Jake Farrell27274222011-11-10 20:32:44 +0000141 public
142 // TJSONProtocolImpl Constructor
Jens Geyer3b686532021-07-01 23:04:08 +0200143 constructor Create( const aTrans : ITransport); override;
Jake Farrell27274222011-11-10 20:32:44 +0000144 destructor Destroy; override;
145
Jens Geyerfad7fd32019-11-09 23:24:52 +0100146 strict protected
Jake Farrell27274222011-11-10 20:32:44 +0000147 // IJSONProtocol
148 // Read a byte that must match b; otherwise an exception is thrown.
149 procedure ReadJSONSyntaxChar( b : Byte);
150
Jens Geyerfad7fd32019-11-09 23:24:52 +0100151 strict private
Jake Farrell27274222011-11-10 20:32:44 +0000152 // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its corresponding hex value
153 class function HexVal( ch : Byte) : Byte;
154
155 // Convert a byte containing a hex value to its corresponding hex character
156 class function HexChar( val : Byte) : Byte;
157
158 // Write the bytes in array buf as a JSON characters, escaping as needed
159 procedure WriteJSONString( const b : TBytes); overload;
Roger Meier333bbf32012-01-08 21:51:08 +0000160 procedure WriteJSONString( const str : string); overload;
Jake Farrell27274222011-11-10 20:32:44 +0000161
162 // Write out number as a JSON value. If the context dictates so, it will be
163 // wrapped in quotes to output as a JSON string.
Roger Meier333bbf32012-01-08 21:51:08 +0000164 procedure WriteJSONInteger( const num : Int64);
Jake Farrell27274222011-11-10 20:32:44 +0000165
166 // Write out a double as a JSON value. If it is NaN or infinity or if the
167 // context dictates escaping, Write out as JSON string.
168 procedure WriteJSONDouble( const num : Double);
169
170 // Write out contents of byte array b as a JSON string with base-64 encoded data
171 procedure WriteJSONBase64( const b : TBytes);
172
173 procedure WriteJSONObjectStart;
174 procedure WriteJSONObjectEnd;
175 procedure WriteJSONArrayStart;
176 procedure WriteJSONArrayEnd;
177
178 public
179 // IProtocol
Jens Geyer17c3ad92017-09-05 20:31:27 +0200180 procedure WriteMessageBegin( const aMsg : TThriftMessage); override;
Jake Farrell27274222011-11-10 20:32:44 +0000181 procedure WriteMessageEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200182 procedure WriteStructBegin( const struc: TThriftStruct); override;
Jake Farrell27274222011-11-10 20:32:44 +0000183 procedure WriteStructEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200184 procedure WriteFieldBegin( const field: TThriftField); override;
Jake Farrell27274222011-11-10 20:32:44 +0000185 procedure WriteFieldEnd; override;
186 procedure WriteFieldStop; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200187 procedure WriteMapBegin( const map: TThriftMap); override;
Jake Farrell27274222011-11-10 20:32:44 +0000188 procedure WriteMapEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200189 procedure WriteListBegin( const list: TThriftList); override;
Jake Farrell27274222011-11-10 20:32:44 +0000190 procedure WriteListEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200191 procedure WriteSetBegin( const set_: TThriftSet ); override;
Jake Farrell27274222011-11-10 20:32:44 +0000192 procedure WriteSetEnd(); override;
193 procedure WriteBool( b: Boolean); override;
194 procedure WriteByte( b: ShortInt); override;
195 procedure WriteI16( i16: SmallInt); override;
196 procedure WriteI32( i32: Integer); override;
Roger Meier333bbf32012-01-08 21:51:08 +0000197 procedure WriteI64( const i64: Int64); override;
198 procedure WriteDouble( const d: Double); override;
Jake Farrell27274222011-11-10 20:32:44 +0000199 procedure WriteString( const s: string ); override;
200 procedure WriteBinary( const b: TBytes); override;
Jens Geyer62445c12022-06-29 00:00:00 +0200201 procedure WriteUuid( const uuid: TGuid); override;
Jake Farrell27274222011-11-10 20:32:44 +0000202 //
Jens Geyer17c3ad92017-09-05 20:31:27 +0200203 function ReadMessageBegin: TThriftMessage; override;
Jake Farrell27274222011-11-10 20:32:44 +0000204 procedure ReadMessageEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200205 function ReadStructBegin: TThriftStruct; override;
Jake Farrell27274222011-11-10 20:32:44 +0000206 procedure ReadStructEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200207 function ReadFieldBegin: TThriftField; override;
Jake Farrell27274222011-11-10 20:32:44 +0000208 procedure ReadFieldEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200209 function ReadMapBegin: TThriftMap; override;
Jake Farrell27274222011-11-10 20:32:44 +0000210 procedure ReadMapEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200211 function ReadListBegin: TThriftList; override;
Jake Farrell27274222011-11-10 20:32:44 +0000212 procedure ReadListEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200213 function ReadSetBegin: TThriftSet; override;
Jake Farrell27274222011-11-10 20:32:44 +0000214 procedure ReadSetEnd(); override;
215 function ReadBool: Boolean; override;
216 function ReadByte: ShortInt; override;
217 function ReadI16: SmallInt; override;
218 function ReadI32: Integer; override;
219 function ReadI64: Int64; override;
220 function ReadDouble:Double; override;
221 function ReadString : string; override;
222 function ReadBinary: TBytes; override;
Jens Geyer62445c12022-06-29 00:00:00 +0200223 function ReadUuid: TGuid; override;
Jake Farrell27274222011-11-10 20:32:44 +0000224
225
Jens Geyerfad7fd32019-11-09 23:24:52 +0100226 strict private
Jake Farrell27274222011-11-10 20:32:44 +0000227 // Reading methods.
228
229 // Read in a JSON string, unescaping as appropriate.
230 // Skip Reading from the context if skipContext is true.
231 function ReadJSONString( skipContext : Boolean) : TBytes;
232
233 // Return true if the given byte could be a valid part of a JSON number.
234 function IsJSONNumeric( b : Byte) : Boolean;
235
236 // Read in a sequence of characters that are all valid in JSON numbers. Does
237 // not do a complete regex check to validate that this is actually a number.
238 function ReadJSONNumericChars : String;
239
240 // Read in a JSON number. If the context dictates, Read in enclosing quotes.
241 function ReadJSONInteger : Int64;
242
243 // Read in a JSON double value. Throw if the value is not wrapped in quotes
244 // when expected or if wrapped in quotes when not expected.
245 function ReadJSONDouble : Double;
246
247 // Read in a JSON string containing base-64 encoded data and decode it.
248 function ReadJSONBase64 : TBytes;
249
250 procedure ReadJSONObjectStart;
251 procedure ReadJSONObjectEnd;
252 procedure ReadJSONArrayStart;
253 procedure ReadJSONArrayEnd;
254 end;
255
256
257implementation
258
259var
260 COMMA : TBytes;
261 COLON : TBytes;
262 LBRACE : TBytes;
263 RBRACE : TBytes;
264 LBRACKET : TBytes;
265 RBRACKET : TBytes;
266 QUOTE : TBytes;
267 BACKSLASH : TBytes;
Jake Farrell27274222011-11-10 20:32:44 +0000268 ESCSEQ : TBytes;
269
270const
271 VERSION = 1;
272 JSON_CHAR_TABLE : array[0..$2F] of Byte
273 = (0,0,0,0, 0,0,0,0, Byte('b'),Byte('t'),Byte('n'),0, Byte('f'),Byte('r'),0,0,
274 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
275 1,1,Byte('"'),1, 1,1,1,1, 1,1,1,1, 1,1,1,1);
276
Jens Geyer21366942013-12-30 22:04:51 +0100277 ESCAPE_CHARS = '"\/btnfr';
278 ESCAPE_CHAR_VALS = '"\/'#8#9#10#12#13;
Jake Farrell27274222011-11-10 20:32:44 +0000279
280 DEF_STRING_SIZE = 16;
281
282 NAME_BOOL = 'tf';
283 NAME_BYTE = 'i8';
284 NAME_I16 = 'i16';
285 NAME_I32 = 'i32';
286 NAME_I64 = 'i64';
287 NAME_DOUBLE = 'dbl';
288 NAME_STRUCT = 'rec';
289 NAME_STRING = 'str';
290 NAME_MAP = 'map';
291 NAME_LIST = 'lst';
292 NAME_SET = 'set';
Jens Geyer62445c12022-06-29 00:00:00 +0200293 NAME_UUID = 'uid';
Jake Farrell27274222011-11-10 20:32:44 +0000294
295 INVARIANT_CULTURE : TFormatSettings
296 = ( ThousandSeparator: ',';
297 DecimalSeparator: '.');
298
299
300
301//--- TJSONProtocolImpl ----------------------
302
303
Roger Meier333bbf32012-01-08 21:51:08 +0000304function TJSONProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
Jake Farrell27274222011-11-10 20:32:44 +0000305begin
Jens Geyera019cda2019-11-09 23:24:52 +0100306 result := TJSONProtocolImpl.Create( trans);
Jake Farrell27274222011-11-10 20:32:44 +0000307end;
308
309class function TJSONProtocolImpl.GetTypeNameForTypeID(typeID : TType) : string;
310begin
311 case typeID of
312 TType.Bool_: result := NAME_BOOL;
313 TType.Byte_: result := NAME_BYTE;
314 TType.I16: result := NAME_I16;
315 TType.I32: result := NAME_I32;
316 TType.I64: result := NAME_I64;
317 TType.Double_: result := NAME_DOUBLE;
318 TType.String_: result := NAME_STRING;
319 TType.Struct: result := NAME_STRUCT;
320 TType.Map: result := NAME_MAP;
321 TType.Set_: result := NAME_SET;
322 TType.List: result := NAME_LIST;
Jens Geyer62445c12022-06-29 00:00:00 +0200323 TType.Uuid: result := NAME_UUID;
Jake Farrell27274222011-11-10 20:32:44 +0000324 else
Jens Geyere0e32402016-04-20 21:50:48 +0200325 raise TProtocolExceptionNotImplemented.Create('Unrecognized type ('+IntToStr(Ord(typeID))+')');
Jake Farrell27274222011-11-10 20:32:44 +0000326 end;
327end;
328
329
330class function TJSONProtocolImpl.GetTypeIDForTypeName( const name : string) : TType;
331begin
332 if name = NAME_BOOL then result := TType.Bool_
333 else if name = NAME_BYTE then result := TType.Byte_
334 else if name = NAME_I16 then result := TType.I16
335 else if name = NAME_I32 then result := TType.I32
336 else if name = NAME_I64 then result := TType.I64
337 else if name = NAME_DOUBLE then result := TType.Double_
338 else if name = NAME_STRUCT then result := TType.Struct
339 else if name = NAME_STRING then result := TType.String_
340 else if name = NAME_MAP then result := TType.Map
341 else if name = NAME_LIST then result := TType.List
342 else if name = NAME_SET then result := TType.Set_
Jens Geyer62445c12022-06-29 00:00:00 +0200343 else if name = NAME_UUID then result := TType.Uuid
Jens Geyere0e32402016-04-20 21:50:48 +0200344 else raise TProtocolExceptionNotImplemented.Create('Unrecognized type ('+name+')');
Jake Farrell27274222011-11-10 20:32:44 +0000345end;
346
347
348constructor TJSONProtocolImpl.TJSONBaseContext.Create( const aProto : IJSONProtocol);
349begin
350 inherited Create;
Roger Meierfebe8452012-06-06 10:32:24 +0000351 FProto := Pointer(aProto);
Jake Farrell27274222011-11-10 20:32:44 +0000352end;
353
354
355procedure TJSONProtocolImpl.TJSONBaseContext.Write;
356begin
357 // nothing
358end;
359
360
361procedure TJSONProtocolImpl.TJSONBaseContext.Read;
362begin
363 // nothing
364end;
365
366
367function TJSONProtocolImpl.TJSONBaseContext.EscapeNumbers : Boolean;
368begin
369 result := FALSE;
370end;
371
372
373constructor TJSONProtocolImpl.TJSONListContext.Create( const aProto : IJSONProtocol);
374begin
375 inherited Create( aProto);
376 FFirst := TRUE;
377end;
378
379
380procedure TJSONProtocolImpl.TJSONListContext.Write;
381begin
382 if FFirst
383 then FFirst := FALSE
Roger Meierfebe8452012-06-06 10:32:24 +0000384 else IJSONProtocol(FProto).Transport.Write( COMMA);
Jake Farrell27274222011-11-10 20:32:44 +0000385end;
386
387
388procedure TJSONProtocolImpl.TJSONListContext.Read;
389begin
390 if FFirst
391 then FFirst := FALSE
Roger Meierfebe8452012-06-06 10:32:24 +0000392 else IJSONProtocol(FProto).ReadJSONSyntaxChar( COMMA[0]);
Jake Farrell27274222011-11-10 20:32:44 +0000393end;
394
395
396constructor TJSONProtocolImpl.TJSONPairContext.Create( const aProto : IJSONProtocol);
397begin
398 inherited Create( aProto);
399 FFirst := TRUE;
400 FColon := TRUE;
401end;
402
403
404procedure TJSONProtocolImpl.TJSONPairContext.Write;
405begin
406 if FFirst then begin
407 FFirst := FALSE;
408 FColon := TRUE;
409 end
410 else begin
411 if FColon
Roger Meierfebe8452012-06-06 10:32:24 +0000412 then IJSONProtocol(FProto).Transport.Write( COLON)
413 else IJSONProtocol(FProto).Transport.Write( COMMA);
Jake Farrell27274222011-11-10 20:32:44 +0000414 FColon := not FColon;
415 end;
416end;
417
418
419procedure TJSONProtocolImpl.TJSONPairContext.Read;
420begin
421 if FFirst then begin
422 FFirst := FALSE;
423 FColon := TRUE;
424 end
425 else begin
426 if FColon
Roger Meierfebe8452012-06-06 10:32:24 +0000427 then IJSONProtocol(FProto).ReadJSONSyntaxChar( COLON[0])
428 else IJSONProtocol(FProto).ReadJSONSyntaxChar( COMMA[0]);
Jake Farrell27274222011-11-10 20:32:44 +0000429 FColon := not FColon;
430 end;
431end;
432
433
434function TJSONProtocolImpl.TJSONPairContext.EscapeNumbers : Boolean;
435begin
436 result := FColon;
437end;
438
439
440constructor TJSONProtocolImpl.TLookaheadReader.Create( const aProto : IJSONProtocol);
441begin
442 inherited Create;
Roger Meierfebe8452012-06-06 10:32:24 +0000443 FProto := Pointer(aProto);
Jake Farrell27274222011-11-10 20:32:44 +0000444 FHasData := FALSE;
445end;
446
447
448function TJSONProtocolImpl.TLookaheadReader.Read : Byte;
449begin
450 if FHasData
451 then FHasData := FALSE
452 else begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200453 IJSONProtocol(FProto).Transport.ReadAll( @FData, SizeOf(FData), 0, 1);
Jake Farrell27274222011-11-10 20:32:44 +0000454 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200455 result := FData;
Jake Farrell27274222011-11-10 20:32:44 +0000456end;
457
458
459function TJSONProtocolImpl.TLookaheadReader.Peek : Byte;
460begin
461 if not FHasData then begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200462 IJSONProtocol(FProto).Transport.ReadAll( @FData, SizeOf(FData), 0, 1);
Jake Farrell27274222011-11-10 20:32:44 +0000463 FHasData := TRUE;
464 end;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200465 result := FData;
Jake Farrell27274222011-11-10 20:32:44 +0000466end;
467
468
Roger Meier333bbf32012-01-08 21:51:08 +0000469constructor TJSONProtocolImpl.Create( const aTrans : ITransport);
Jake Farrell27274222011-11-10 20:32:44 +0000470begin
471 inherited Create( aTrans);
472
473 // Stack of nested contexts that we may be in
474 FContextStack := TStack<TJSONBaseContext>.Create;
475
476 FContext := TJSONBaseContext.Create( Self);
477 FReader := TLookaheadReader.Create( Self);
478end;
479
480
481destructor TJSONProtocolImpl.Destroy;
482begin
483 try
Roger Meier45a37262012-01-08 21:44:44 +0000484 ResetContextStack; // free any contents
Jake Farrell27274222011-11-10 20:32:44 +0000485 FreeAndNil( FReader);
486 FreeAndNil( FContext);
487 FreeAndNil( FContextStack);
488 finally
489 inherited Destroy;
490 end;
491end;
492
493
Jens Geyer41f47af2019-11-09 23:24:52 +0100494procedure TJSONProtocolImpl.Reset;
495begin
496 inherited Reset;
497 ResetContextStack;
498end;
499
500
Roger Meier45a37262012-01-08 21:44:44 +0000501procedure TJSONProtocolImpl.ResetContextStack;
502begin
503 while FContextStack.Count > 0
504 do PopContext;
505end;
506
507
Roger Meier333bbf32012-01-08 21:51:08 +0000508procedure TJSONProtocolImpl.PushContext( const aCtx : TJSONBaseContext);
Roger Meier45a37262012-01-08 21:44:44 +0000509begin
510 FContextStack.Push( FContext);
511 FContext := aCtx;
512end;
513
514
515procedure TJSONProtocolImpl.PopContext;
516begin
517 FreeAndNil(FContext);
518 FContext := FContextStack.Pop;
519end;
520
521
Jake Farrell27274222011-11-10 20:32:44 +0000522procedure TJSONProtocolImpl.ReadJSONSyntaxChar( b : Byte);
523var ch : Byte;
524begin
525 ch := FReader.Read;
526 if (ch <> b)
Jens Geyere0e32402016-04-20 21:50:48 +0200527 then raise TProtocolExceptionInvalidData.Create('Unexpected character ('+Char(ch)+')');
Jake Farrell27274222011-11-10 20:32:44 +0000528end;
529
530
531class function TJSONProtocolImpl.HexVal( ch : Byte) : Byte;
532var i : Integer;
533begin
534 i := StrToIntDef( '$0'+Char(ch), -1);
535 if (0 <= i) and (i < $10)
536 then result := i
Jens Geyere0e32402016-04-20 21:50:48 +0200537 else raise TProtocolExceptionInvalidData.Create('Expected hex character ('+Char(ch)+')');
Jake Farrell27274222011-11-10 20:32:44 +0000538end;
539
540
541class function TJSONProtocolImpl.HexChar( val : Byte) : Byte;
542const HEXCHARS = '0123456789ABCDEF';
543begin
544 result := Byte( PChar(HEXCHARS)[val and $0F]);
545 ASSERT( Pos( Char(result), HEXCHARS) > 0);
546end;
547
548
Roger Meier333bbf32012-01-08 21:51:08 +0000549procedure TJSONProtocolImpl.WriteJSONString( const str : string);
Jake Farrell27274222011-11-10 20:32:44 +0000550begin
551 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( str));
552end;
553
554
555procedure TJSONProtocolImpl.WriteJSONString( const b : TBytes);
556var i : Integer;
557 tmp : TBytes;
558begin
559 FContext.Write;
560 Transport.Write( QUOTE);
561 for i := 0 to Length(b)-1 do begin
562
563 if (b[i] and $00FF) >= $30 then begin
564
565 if (b[i] = BACKSLASH[0]) then begin
566 Transport.Write( BACKSLASH);
567 Transport.Write( BACKSLASH);
568 end
569 else begin
570 Transport.Write( b, i, 1);
571 end;
572
573 end
574 else begin
575 SetLength( tmp, 2);
576 tmp[0] := JSON_CHAR_TABLE[b[i]];
577 if (tmp[0] = 1) then begin
578 Transport.Write( b, i, 1)
579 end
580 else if (tmp[0] > 1) then begin
581 Transport.Write( BACKSLASH);
582 Transport.Write( tmp, 0, 1);
583 end
584 else begin
585 Transport.Write( ESCSEQ);
586 tmp[0] := HexChar( b[i] div $10);
587 tmp[1] := HexChar( b[i]);
588 Transport.Write( tmp, 0, 2);
589 end;
590 end;
591 end;
592 Transport.Write( QUOTE);
593end;
594
595
Roger Meier333bbf32012-01-08 21:51:08 +0000596procedure TJSONProtocolImpl.WriteJSONInteger( const num : Int64);
Jake Farrell27274222011-11-10 20:32:44 +0000597var str : String;
598 escapeNum : Boolean;
599begin
600 FContext.Write;
601 str := IntToStr(num);
602
603 escapeNum := FContext.EscapeNumbers;
604 if escapeNum
605 then Transport.Write( QUOTE);
606
607 Transport.Write( SysUtils.TEncoding.UTF8.GetBytes( str));
608
609 if escapeNum
610 then Transport.Write( QUOTE);
611end;
612
613
614procedure TJSONProtocolImpl.WriteJSONDouble( const num : Double);
615var str : string;
616 special : Boolean;
617 escapeNum : Boolean;
618begin
619 FContext.Write;
620
621 str := FloatToStr( num, INVARIANT_CULTURE);
622 special := FALSE;
623
624 case UpCase(str[1]) of
625 'N' : special := TRUE; // NaN
626 'I' : special := TRUE; // Infinity
627 '-' : special := (UpCase(str[2]) = 'I'); // -Infinity
628 end;
629
630 escapeNum := special or FContext.EscapeNumbers;
631
632
633 if escapeNum
634 then Transport.Write( QUOTE);
635
636 Transport.Write( SysUtils.TEncoding.UTF8.GetBytes( str));
637
638 if escapeNum
639 then Transport.Write( QUOTE);
640end;
641
642
643procedure TJSONProtocolImpl.WriteJSONBase64( const b : TBytes);
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100644var len, off, cnt : Integer;
645 tmpBuf : TBytes;
Jake Farrell27274222011-11-10 20:32:44 +0000646begin
647 FContext.Write;
648 Transport.Write( QUOTE);
649
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100650 len := Length(b);
651 off := 0;
652 SetLength( tmpBuf, 4);
653
654 while len >= 3 do begin
655 // Encode 3 bytes at a time
656 Base64Utils.Encode( b, off, 3, tmpBuf, 0);
657 Transport.Write( tmpBuf, 0, 4);
658 Inc( off, 3);
659 Dec( len, 3);
Jake Farrell27274222011-11-10 20:32:44 +0000660 end;
Jens Geyerd8bddbc2014-12-14 00:41:33 +0100661
662 // Encode remainder, if any
663 if len > 0 then begin
664 cnt := Base64Utils.Encode( b, off, len, tmpBuf, 0);
665 Transport.Write( tmpBuf, 0, cnt);
666 end;
Jake Farrell27274222011-11-10 20:32:44 +0000667
668 Transport.Write( QUOTE);
669end;
670
671
672procedure TJSONProtocolImpl.WriteJSONObjectStart;
673begin
674 FContext.Write;
675 Transport.Write( LBRACE);
676 PushContext( TJSONPairContext.Create( Self));
677end;
678
679
680procedure TJSONProtocolImpl.WriteJSONObjectEnd;
681begin
682 PopContext;
683 Transport.Write( RBRACE);
684end;
685
686
687procedure TJSONProtocolImpl.WriteJSONArrayStart;
688begin
689 FContext.Write;
690 Transport.Write( LBRACKET);
691 PushContext( TJSONListContext.Create( Self));
692end;
693
694
695procedure TJSONProtocolImpl.WriteJSONArrayEnd;
696begin
697 PopContext;
698 Transport.Write( RBRACKET);
699end;
700
701
Jens Geyer17c3ad92017-09-05 20:31:27 +0200702procedure TJSONProtocolImpl.WriteMessageBegin( const aMsg : TThriftMessage);
Jake Farrell27274222011-11-10 20:32:44 +0000703begin
Jens Geyer41f47af2019-11-09 23:24:52 +0100704 Reset;
Roger Meier45a37262012-01-08 21:44:44 +0000705 ResetContextStack; // THRIFT-1473
706
Jake Farrell27274222011-11-10 20:32:44 +0000707 WriteJSONArrayStart;
708 WriteJSONInteger(VERSION);
709
710 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( aMsg.Name));
711
712 WriteJSONInteger( LongInt( aMsg.Type_));
713 WriteJSONInteger( aMsg.SeqID);
714end;
715
716procedure TJSONProtocolImpl.WriteMessageEnd;
717begin
718 WriteJSONArrayEnd;
719end;
720
721
Jens Geyer17c3ad92017-09-05 20:31:27 +0200722procedure TJSONProtocolImpl.WriteStructBegin( const struc: TThriftStruct);
Jake Farrell27274222011-11-10 20:32:44 +0000723begin
724 WriteJSONObjectStart;
725end;
726
727
728procedure TJSONProtocolImpl.WriteStructEnd;
729begin
730 WriteJSONObjectEnd;
731end;
732
733
Jens Geyer17c3ad92017-09-05 20:31:27 +0200734procedure TJSONProtocolImpl.WriteFieldBegin( const field : TThriftField);
Jake Farrell27274222011-11-10 20:32:44 +0000735begin
736 WriteJSONInteger(field.ID);
737 WriteJSONObjectStart;
738 WriteJSONString( GetTypeNameForTypeID(field.Type_));
739end;
740
741
742procedure TJSONProtocolImpl.WriteFieldEnd;
743begin
744 WriteJSONObjectEnd;
745end;
746
747
748procedure TJSONProtocolImpl.WriteFieldStop;
749begin
750 // nothing to do
751end;
752
Jens Geyer17c3ad92017-09-05 20:31:27 +0200753procedure TJSONProtocolImpl.WriteMapBegin( const map: TThriftMap);
Jake Farrell27274222011-11-10 20:32:44 +0000754begin
755 WriteJSONArrayStart;
756 WriteJSONString( GetTypeNameForTypeID( map.KeyType));
757 WriteJSONString( GetTypeNameForTypeID( map.ValueType));
758 WriteJSONInteger( map.Count);
759 WriteJSONObjectStart;
760end;
761
762
763procedure TJSONProtocolImpl.WriteMapEnd;
764begin
765 WriteJSONObjectEnd;
766 WriteJSONArrayEnd;
767end;
768
769
Jens Geyer17c3ad92017-09-05 20:31:27 +0200770procedure TJSONProtocolImpl.WriteListBegin( const list: TThriftList);
Jake Farrell27274222011-11-10 20:32:44 +0000771begin
772 WriteJSONArrayStart;
773 WriteJSONString( GetTypeNameForTypeID( list.ElementType));
774 WriteJSONInteger(list.Count);
775end;
776
777
778procedure TJSONProtocolImpl.WriteListEnd;
779begin
780 WriteJSONArrayEnd;
781end;
782
783
Jens Geyer17c3ad92017-09-05 20:31:27 +0200784procedure TJSONProtocolImpl.WriteSetBegin( const set_: TThriftSet);
Jake Farrell27274222011-11-10 20:32:44 +0000785begin
786 WriteJSONArrayStart;
787 WriteJSONString( GetTypeNameForTypeID( set_.ElementType));
788 WriteJSONInteger( set_.Count);
789end;
790
791
792procedure TJSONProtocolImpl.WriteSetEnd;
793begin
794 WriteJSONArrayEnd;
795end;
796
797procedure TJSONProtocolImpl.WriteBool( b: Boolean);
798begin
799 if b
800 then WriteJSONInteger( 1)
801 else WriteJSONInteger( 0);
802end;
803
804procedure TJSONProtocolImpl.WriteByte( b: ShortInt);
805begin
806 WriteJSONInteger( b);
807end;
808
809procedure TJSONProtocolImpl.WriteI16( i16: SmallInt);
810begin
811 WriteJSONInteger( i16);
812end;
813
814procedure TJSONProtocolImpl.WriteI32( i32: Integer);
815begin
816 WriteJSONInteger( i32);
817end;
818
Roger Meier333bbf32012-01-08 21:51:08 +0000819procedure TJSONProtocolImpl.WriteI64( const i64: Int64);
Jake Farrell27274222011-11-10 20:32:44 +0000820begin
821 WriteJSONInteger(i64);
822end;
823
Roger Meier333bbf32012-01-08 21:51:08 +0000824procedure TJSONProtocolImpl.WriteDouble( const d: Double);
Jake Farrell27274222011-11-10 20:32:44 +0000825begin
826 WriteJSONDouble( d);
827end;
828
829procedure TJSONProtocolImpl.WriteString( const s: string );
830begin
831 WriteJSONString( SysUtils.TEncoding.UTF8.GetBytes( s));
832end;
833
834procedure TJSONProtocolImpl.WriteBinary( const b: TBytes);
835begin
836 WriteJSONBase64( b);
837end;
838
Jens Geyer62445c12022-06-29 00:00:00 +0200839procedure TJSONProtocolImpl.WriteUuid( const uuid: TGuid);
840begin
841 WriteString( Copy( GuidToString(uuid), 2, 36)); // strip off the { braces }
842end;
843
Jake Farrell27274222011-11-10 20:32:44 +0000844
845function TJSONProtocolImpl.ReadJSONString( skipContext : Boolean) : TBytes;
Jens Geyerf726ae32021-06-04 11:17:26 +0200846var buffer : TThriftMemoryStream;
Jens Geyer7bb44a32014-02-07 22:24:37 +0100847 ch : Byte;
848 wch : Word;
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700849 highSurogate: Char;
850 surrogatePairs: Array[0..1] of Char;
Jake Farrell27274222011-11-10 20:32:44 +0000851 off : Integer;
852 tmp : TBytes;
853begin
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700854 highSurogate := #0;
Jens Geyerf726ae32021-06-04 11:17:26 +0200855 buffer := TThriftMemoryStream.Create;
Jake Farrell27274222011-11-10 20:32:44 +0000856 try
857 if not skipContext
858 then FContext.Read;
859
860 ReadJSONSyntaxChar( QUOTE[0]);
861
862 while TRUE do begin
863 ch := FReader.Read;
864
865 if (ch = QUOTE[0])
866 then Break;
867
Jens Geyer7bb44a32014-02-07 22:24:37 +0100868 // check for escapes
869 if (ch <> ESCSEQ[0]) then begin
870 buffer.Write( ch, 1);
871 Continue;
Jake Farrell27274222011-11-10 20:32:44 +0000872 end;
Jens Geyer7bb44a32014-02-07 22:24:37 +0100873
874 // distuinguish between \uNNNN and \?
875 ch := FReader.Read;
876 if (ch <> ESCSEQ[1])
877 then begin
878 off := Pos( Char(ch), ESCAPE_CHARS);
879 if off < 1
Jens Geyere0e32402016-04-20 21:50:48 +0200880 then raise TProtocolExceptionInvalidData.Create('Expected control char');
Jens Geyer7bb44a32014-02-07 22:24:37 +0100881 ch := Byte( ESCAPE_CHAR_VALS[off]);
882 buffer.Write( ch, 1);
883 Continue;
884 end;
885
886 // it is \uXXXX
887 SetLength( tmp, 4);
888 Transport.ReadAll( tmp, 0, 4);
889 wch := (HexVal(tmp[0]) shl 12)
890 + (HexVal(tmp[1]) shl 8)
891 + (HexVal(tmp[2]) shl 4)
892 + HexVal(tmp[3]);
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700893
Jens Geyer7bb44a32014-02-07 22:24:37 +0100894 // we need to make UTF8 bytes from it, to be decoded later
Jens Geyer71070432016-01-29 10:08:39 +0100895 if CharUtils.IsHighSurrogate(char(wch)) then begin
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700896 if highSurogate <> #0
Jens Geyere0e32402016-04-20 21:50:48 +0200897 then raise TProtocolExceptionInvalidData.Create('Expected low surrogate char');
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700898 highSurogate := char(wch);
899 end
Jens Geyer71070432016-01-29 10:08:39 +0100900 else if CharUtils.IsLowSurrogate(char(wch)) then begin
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700901 if highSurogate = #0
Jens Geyere0e32402016-04-20 21:50:48 +0200902 then TProtocolExceptionInvalidData.Create('Expected high surrogate char');
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700903 surrogatePairs[0] := highSurogate;
904 surrogatePairs[1] := char(wch);
905 tmp := TEncoding.UTF8.GetBytes(surrogatePairs);
906 buffer.Write( tmp[0], Length(tmp));
907 highSurogate := #0;
908 end
909 else begin
910 tmp := SysUtils.TEncoding.UTF8.GetBytes(Char(wch));
911 buffer.Write( tmp[0], Length(tmp));
912 end;
Jake Farrell27274222011-11-10 20:32:44 +0000913 end;
914
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700915 if highSurogate <> #0
Jens Geyere0e32402016-04-20 21:50:48 +0200916 then raise TProtocolExceptionInvalidData.Create('Expected low surrogate char');
Phongphan Phutthaa6509f72015-10-31 01:09:47 +0700917
Jake Farrell27274222011-11-10 20:32:44 +0000918 SetLength( result, buffer.Size);
Jake Farrella2a9ee92011-12-15 20:50:31 +0000919 if buffer.Size > 0 then Move( buffer.Memory^, result[0], Length(result));
Jake Farrell27274222011-11-10 20:32:44 +0000920
921 finally
922 buffer.Free;
923 end;
924end;
925
926
927function TJSONProtocolImpl.IsJSONNumeric( b : Byte) : Boolean;
928const NUMCHARS = ['+','-','.','0','1','2','3','4','5','6','7','8','9','E','e'];
929begin
930 result := CharInSet( Char(b), NUMCHARS);
931end;
932
933
934function TJSONProtocolImpl.ReadJSONNumericChars : string;
935var strbld : TThriftStringBuilder;
936 ch : Byte;
937begin
938 strbld := TThriftStringBuilder.Create;
939 try
940 while TRUE do begin
941 ch := FReader.Peek;
942 if IsJSONNumeric(ch)
943 then strbld.Append( Char(FReader.Read))
944 else Break;
945 end;
946 result := strbld.ToString;
947
948 finally
949 strbld.Free;
950 end;
951end;
952
953
954function TJSONProtocolImpl.ReadJSONInteger : Int64;
955var str : string;
956begin
957 FContext.Read;
958 if FContext.EscapeNumbers
959 then ReadJSONSyntaxChar( QUOTE[0]);
960
961 str := ReadJSONNumericChars;
962
963 if FContext.EscapeNumbers
964 then ReadJSONSyntaxChar( QUOTE[0]);
965
966 try
967 result := StrToInt64(str);
968 except
969 on e:Exception do begin
Jens Geyere0e32402016-04-20 21:50:48 +0200970 raise TProtocolExceptionInvalidData.Create('Bad data encounted in numeric data ('+str+') ('+e.Message+')');
Jake Farrell27274222011-11-10 20:32:44 +0000971 end;
972 end;
973end;
974
975
976function TJSONProtocolImpl.ReadJSONDouble : Double;
977var dub : Double;
978 str : string;
979begin
980 FContext.Read;
981
982 if FReader.Peek = QUOTE[0]
983 then begin
984 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( TRUE));
985 dub := StrToFloat( str, INVARIANT_CULTURE);
986
987 if not FContext.EscapeNumbers()
988 and not Math.IsNaN(dub)
989 and not Math.IsInfinite(dub)
990 then begin
991 // Throw exception -- we should not be in a string in Self case
Jens Geyere0e32402016-04-20 21:50:48 +0200992 raise TProtocolExceptionInvalidData.Create('Numeric data unexpectedly quoted');
Jake Farrell27274222011-11-10 20:32:44 +0000993 end;
994 result := dub;
995 Exit;
996 end;
997
998 // will throw - we should have had a quote if escapeNum == true
999 if FContext.EscapeNumbers
1000 then ReadJSONSyntaxChar( QUOTE[0]);
1001
1002 try
1003 str := ReadJSONNumericChars;
1004 result := StrToFloat( str, INVARIANT_CULTURE);
1005 except
1006 on e:Exception
Jens Geyere0e32402016-04-20 21:50:48 +02001007 do raise TProtocolExceptionInvalidData.Create('Bad data encounted in numeric data ('+str+') ('+e.Message+')');
Jake Farrell27274222011-11-10 20:32:44 +00001008 end;
1009end;
1010
1011
1012function TJSONProtocolImpl.ReadJSONBase64 : TBytes;
1013var b : TBytes;
Jens Geyer9f9535c2014-12-14 04:16:05 +01001014 len, off, size : Integer;
Jake Farrell27274222011-11-10 20:32:44 +00001015begin
1016 b := ReadJSONString(false);
1017
Jens Geyerd8bddbc2014-12-14 00:41:33 +01001018 len := Length(b);
1019 off := 0;
1020 size := 0;
1021
1022 // reduce len to ignore fill bytes
1023 Dec(len);
1024 while (len >= 0) and (b[len] = Byte('=')) do Dec(len);
1025 Inc(len);
1026
1027 // read & decode full byte triplets = 4 source bytes
1028 while (len >= 4) do begin
1029 // Decode 4 bytes at a time
1030 Inc( size, Base64Utils.Decode( b, off, 4, b, size)); // decoded in place
1031 Inc( off, 4);
1032 Dec( len, 4);
1033 end;
1034
1035 // Don't decode if we hit the end or got a single leftover byte (invalid
1036 // base64 but legal for skip of regular string type)
1037 if len > 1 then begin
1038 // Decode remainder
1039 Inc( size, Base64Utils.Decode( b, off, len, b, size)); // decoded in place
1040 end;
1041
1042 // resize to final size and return the data
1043 SetLength( b, size);
1044 result := b;
Jake Farrell27274222011-11-10 20:32:44 +00001045end;
1046
1047
1048procedure TJSONProtocolImpl.ReadJSONObjectStart;
1049begin
1050 FContext.Read;
1051 ReadJSONSyntaxChar( LBRACE[0]);
1052 PushContext( TJSONPairContext.Create( Self));
1053end;
1054
1055
1056procedure TJSONProtocolImpl.ReadJSONObjectEnd;
1057begin
1058 ReadJSONSyntaxChar( RBRACE[0]);
1059 PopContext;
1060end;
1061
1062
1063procedure TJSONProtocolImpl.ReadJSONArrayStart;
1064begin
1065 FContext.Read;
1066 ReadJSONSyntaxChar( LBRACKET[0]);
1067 PushContext( TJSONListContext.Create( Self));
1068end;
1069
1070
1071procedure TJSONProtocolImpl.ReadJSONArrayEnd;
1072begin
1073 ReadJSONSyntaxChar( RBRACKET[0]);
1074 PopContext;
1075end;
1076
1077
Jens Geyer17c3ad92017-09-05 20:31:27 +02001078function TJSONProtocolImpl.ReadMessageBegin: TThriftMessage;
Jake Farrell27274222011-11-10 20:32:44 +00001079begin
Jens Geyer41f47af2019-11-09 23:24:52 +01001080 Reset;
Roger Meier45a37262012-01-08 21:44:44 +00001081 ResetContextStack; // THRIFT-1473
1082
Jens Geyer17c3ad92017-09-05 20:31:27 +02001083 Init( result);
Jake Farrell27274222011-11-10 20:32:44 +00001084 ReadJSONArrayStart;
1085
1086 if ReadJSONInteger <> VERSION
Jens Geyere0e32402016-04-20 21:50:48 +02001087 then raise TProtocolExceptionBadVersion.Create('Message contained bad version.');
Jake Farrell27274222011-11-10 20:32:44 +00001088
1089 result.Name := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
1090 result.Type_ := TMessageType( ReadJSONInteger);
1091 result.SeqID := ReadJSONInteger;
1092end;
1093
1094
1095procedure TJSONProtocolImpl.ReadMessageEnd;
1096begin
1097 ReadJSONArrayEnd;
1098end;
1099
1100
Jens Geyer17c3ad92017-09-05 20:31:27 +02001101function TJSONProtocolImpl.ReadStructBegin : TThriftStruct ;
Jake Farrell27274222011-11-10 20:32:44 +00001102begin
1103 ReadJSONObjectStart;
Jens Geyer17c3ad92017-09-05 20:31:27 +02001104 Init( result);
Jake Farrell27274222011-11-10 20:32:44 +00001105end;
1106
1107
1108procedure TJSONProtocolImpl.ReadStructEnd;
1109begin
1110 ReadJSONObjectEnd;
1111end;
1112
1113
Jens Geyer17c3ad92017-09-05 20:31:27 +02001114function TJSONProtocolImpl.ReadFieldBegin : TThriftField;
Jake Farrell27274222011-11-10 20:32:44 +00001115var ch : Byte;
1116 str : string;
1117begin
Jens Geyer17c3ad92017-09-05 20:31:27 +02001118 Init( result);
Jake Farrell27274222011-11-10 20:32:44 +00001119 ch := FReader.Peek;
1120 if ch = RBRACE[0]
1121 then result.Type_ := TType.Stop
1122 else begin
1123 result.ID := ReadJSONInteger;
1124 ReadJSONObjectStart;
1125
1126 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
1127 result.Type_ := GetTypeIDForTypeName( str);
1128 end;
1129end;
1130
1131
1132procedure TJSONProtocolImpl.ReadFieldEnd;
1133begin
1134 ReadJSONObjectEnd;
1135end;
1136
1137
Jens Geyer17c3ad92017-09-05 20:31:27 +02001138function TJSONProtocolImpl.ReadMapBegin : TThriftMap;
Jake Farrell27274222011-11-10 20:32:44 +00001139var str : string;
1140begin
Jens Geyer17c3ad92017-09-05 20:31:27 +02001141 Init( result);
Jake Farrell27274222011-11-10 20:32:44 +00001142 ReadJSONArrayStart;
1143
1144 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1145 result.KeyType := GetTypeIDForTypeName( str);
1146
1147 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1148 result.ValueType := GetTypeIDForTypeName( str);
1149
1150 result.Count := ReadJSONInteger;
Jens Geyer41f47af2019-11-09 23:24:52 +01001151 CheckReadBytesAvailable(result);
1152
Jake Farrell27274222011-11-10 20:32:44 +00001153 ReadJSONObjectStart;
1154end;
1155
1156
1157procedure TJSONProtocolImpl.ReadMapEnd;
1158begin
1159 ReadJSONObjectEnd;
1160 ReadJSONArrayEnd;
1161end;
1162
1163
Jens Geyer17c3ad92017-09-05 20:31:27 +02001164function TJSONProtocolImpl.ReadListBegin : TThriftList;
Jake Farrell27274222011-11-10 20:32:44 +00001165var str : string;
1166begin
Jens Geyer17c3ad92017-09-05 20:31:27 +02001167 Init( result);
Jake Farrell27274222011-11-10 20:32:44 +00001168 ReadJSONArrayStart;
1169
1170 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1171 result.ElementType := GetTypeIDForTypeName( str);
1172 result.Count := ReadJSONInteger;
Jens Geyer41f47af2019-11-09 23:24:52 +01001173 CheckReadBytesAvailable(result);
Jake Farrell27274222011-11-10 20:32:44 +00001174end;
1175
1176
1177procedure TJSONProtocolImpl.ReadListEnd;
1178begin
1179 ReadJSONArrayEnd;
1180end;
1181
1182
Jens Geyer17c3ad92017-09-05 20:31:27 +02001183function TJSONProtocolImpl.ReadSetBegin : TThriftSet;
Jake Farrell27274222011-11-10 20:32:44 +00001184var str : string;
1185begin
Jens Geyer17c3ad92017-09-05 20:31:27 +02001186 Init( result);
Jake Farrell27274222011-11-10 20:32:44 +00001187 ReadJSONArrayStart;
1188
1189 str := SysUtils.TEncoding.UTF8.GetString( ReadJSONString(FALSE));
1190 result.ElementType := GetTypeIDForTypeName( str);
1191 result.Count := ReadJSONInteger;
Jens Geyer41f47af2019-11-09 23:24:52 +01001192 CheckReadBytesAvailable(result);
Jake Farrell27274222011-11-10 20:32:44 +00001193end;
1194
1195
1196procedure TJSONProtocolImpl.ReadSetEnd;
1197begin
1198 ReadJSONArrayEnd;
1199end;
1200
1201
1202function TJSONProtocolImpl.ReadBool : Boolean;
1203begin
1204 result := (ReadJSONInteger <> 0);
1205end;
1206
1207
1208function TJSONProtocolImpl.ReadByte : ShortInt;
1209begin
1210 result := ReadJSONInteger;
1211end;
1212
1213
1214function TJSONProtocolImpl.ReadI16 : SmallInt;
1215begin
1216 result := ReadJSONInteger;
1217end;
1218
1219
1220function TJSONProtocolImpl.ReadI32 : LongInt;
1221begin
1222 result := ReadJSONInteger;
1223end;
1224
1225
1226function TJSONProtocolImpl.ReadI64 : Int64;
1227begin
1228 result := ReadJSONInteger;
1229end;
1230
1231
1232function TJSONProtocolImpl.ReadDouble : Double;
1233begin
1234 result := ReadJSONDouble;
1235end;
1236
1237
1238function TJSONProtocolImpl.ReadString : string;
1239begin
1240 result := SysUtils.TEncoding.UTF8.GetString( ReadJSONString( FALSE));
1241end;
1242
1243
1244function TJSONProtocolImpl.ReadBinary : TBytes;
1245begin
1246 result := ReadJSONBase64;
1247end;
1248
1249
Jens Geyer62445c12022-06-29 00:00:00 +02001250function TJSONProtocolImpl.ReadUuid: TGuid;
1251begin
1252 result := StringToGUID( '{' + ReadString + '}');
1253end;
1254
1255
Jens Geyer41f47af2019-11-09 23:24:52 +01001256function TJSONProtocolImpl.GetMinSerializedSize( const aType : TType) : Integer;
1257// Return the minimum number of bytes a type will consume on the wire
1258begin
1259 case aType of
1260 TType.Stop: result := 0;
1261 TType.Void: result := 0;
1262 TType.Bool_: result := 1;
1263 TType.Byte_: result := 1;
1264 TType.Double_: result := 1;
1265 TType.I16: result := 1;
1266 TType.I32: result := 1;
1267 TType.I64: result := 1;
1268 TType.String_: result := 2; // empty string
1269 TType.Struct: result := 2; // empty struct
1270 TType.Map: result := 2; // empty map
1271 TType.Set_: result := 2; // empty set
1272 TType.List: result := 2; // empty list
Jens Geyer62445c12022-06-29 00:00:00 +02001273 TType.Uuid: result := 36; // "E236974D-F0B0-4E05-8F29-0B455D41B1A1"
Jens Geyer41f47af2019-11-09 23:24:52 +01001274 else
1275 raise TTransportExceptionBadArgs.Create('Unhandled type code');
1276 end;
1277end;
1278
1279
1280
Jake Farrell27274222011-11-10 20:32:44 +00001281//--- init code ---
1282
1283procedure InitBytes( var b : TBytes; aData : array of Byte);
1284begin
1285 SetLength( b, Length(aData));
1286 Move( aData, b[0], Length(b));
1287end;
1288
1289initialization
1290 InitBytes( COMMA, [Byte(',')]);
1291 InitBytes( COLON, [Byte(':')]);
1292 InitBytes( LBRACE, [Byte('{')]);
1293 InitBytes( RBRACE, [Byte('}')]);
1294 InitBytes( LBRACKET, [Byte('[')]);
1295 InitBytes( RBRACKET, [Byte(']')]);
1296 InitBytes( QUOTE, [Byte('"')]);
1297 InitBytes( BACKSLASH, [Byte('\')]);
Jake Farrell27274222011-11-10 20:32:44 +00001298 InitBytes( ESCSEQ, [Byte('\'),Byte('u'),Byte('0'),Byte('0')]);
1299end.