blob: 02a19ea8067fc5654259743c0360c82dc7ff0fcf [file] [log] [blame]
Jens Geyerf0e63312015-03-01 18:47:49 +01001(*
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.Compact;
23
24interface
25
26uses
27 Classes,
28 SysUtils,
29 Math,
30 Generics.Collections,
Jens Geyera019cda2019-11-09 23:24:52 +010031 Thrift.Configuration,
Jens Geyerf0e63312015-03-01 18:47:49 +010032 Thrift.Transport,
33 Thrift.Protocol,
34 Thrift.Utils;
35
36type
37 ICompactProtocol = interface( IProtocol)
38 ['{C01927EC-021A-45F7-93B1-23D6A5420EDD}']
39 end;
40
41 // Compact protocol implementation for thrift.
42 // Adapted from the C# version.
43 TCompactProtocolImpl = class( TProtocolImpl, ICompactProtocol)
44 public
45 type
46 TFactory = class( TInterfacedObject, IProtocolFactory)
47 public
48 function GetProtocol( const trans: ITransport): IProtocol;
49 end;
50
Jens Geyerfad7fd32019-11-09 23:24:52 +010051 strict private const
Jens Geyerf0e63312015-03-01 18:47:49 +010052
53 { TODO
54 static TStruct ANONYMOUS_STRUCT = new TStruct("");
55 static TField TSTOP = new TField("", TType.Stop, (short)0);
56 }
57
58 PROTOCOL_ID = Byte( $82);
59 VERSION = Byte( 1);
60 VERSION_MASK = Byte( $1F); // 0001 1111
61 TYPE_MASK = Byte( $E0); // 1110 0000
62 TYPE_BITS = Byte( $07); // 0000 0111
63 TYPE_SHIFT_AMOUNT = Byte( 5);
64
Jens Geyerfad7fd32019-11-09 23:24:52 +010065 strict private type
Jens Geyerf0e63312015-03-01 18:47:49 +010066 // All of the on-wire type codes.
67 Types = (
68 STOP = $00,
69 BOOLEAN_TRUE = $01,
70 BOOLEAN_FALSE = $02,
71 BYTE_ = $03,
72 I16 = $04,
73 I32 = $05,
74 I64 = $06,
75 DOUBLE_ = $07,
76 BINARY = $08,
77 LIST = $09,
78 SET_ = $0A,
79 MAP = $0B,
Jens Geyer62445c12022-06-29 00:00:00 +020080 STRUCT = $0C,
81 UUID = $0D
Jens Geyerf0e63312015-03-01 18:47:49 +010082 );
83
Jens Geyerf726ae32021-06-04 11:17:26 +020084 private type
85 TEightBytesArray = packed array[0..7] of Byte;
86
Jens Geyerfad7fd32019-11-09 23:24:52 +010087 strict private const
Jens Geyerf0e63312015-03-01 18:47:49 +010088 ttypeToCompactType : array[TType] of Types = (
89 Types.STOP, // Stop = 0,
90 Types(-1), // Void = 1,
91 Types.BOOLEAN_TRUE, // Bool_ = 2,
92 Types.BYTE_, // Byte_ = 3,
93 Types.DOUBLE_, // Double_ = 4,
94 Types(-5), // unused
95 Types.I16, // I16 = 6,
96 Types(-7), // unused
97 Types.I32, // I32 = 8,
98 Types(-9), // unused
99 Types.I64, // I64 = 10,
100 Types.BINARY, // String_ = 11,
101 Types.STRUCT, // Struct = 12,
102 Types.MAP, // Map = 13,
103 Types.SET_, // Set_ = 14,
Jens Geyer62445c12022-06-29 00:00:00 +0200104 Types.LIST, // List = 15,
105 Types.UUID // Uuid = 16
Jens Geyerf0e63312015-03-01 18:47:49 +0100106 );
107
108 tcompactTypeToType : array[Types] of TType = (
109 TType.Stop, // STOP
110 TType.Bool_, // BOOLEAN_TRUE
111 TType.Bool_, // BOOLEAN_FALSE
112 TType.Byte_, // BYTE_
113 TType.I16, // I16
114 TType.I32, // I32
115 TType.I64, // I64
116 TType.Double_, // DOUBLE_
117 TType.String_, // BINARY
118 TType.List, // LIST
119 TType.Set_, // SET_
120 TType.Map, // MAP
Jens Geyer62445c12022-06-29 00:00:00 +0200121 TType.Struct, // STRUCT
122 TType.Uuid // UUID
Jens Geyerf0e63312015-03-01 18:47:49 +0100123 );
124
Jens Geyerfad7fd32019-11-09 23:24:52 +0100125 strict private
Jens Geyerf0e63312015-03-01 18:47:49 +0100126 // Used to keep track of the last field for the current and previous structs,
127 // so we can do the delta stuff.
128 lastField_ : TStack<Integer>;
129 lastFieldId_ : Integer;
130
131 // If we encounter a boolean field begin, save the TField here so it can
132 // have the value incorporated.
Jens Geyerfad7fd32019-11-09 23:24:52 +0100133 strict private booleanField_ : TThriftField;
Jens Geyerf0e63312015-03-01 18:47:49 +0100134
135 // If we Read a field header, and it's a boolean field, save the boolean
136 // value here so that ReadBool can use it.
Jens Geyerfad7fd32019-11-09 23:24:52 +0100137 strict private boolValue_ : ( unused, bool_true, bool_false);
Jens Geyerf0e63312015-03-01 18:47:49 +0100138
139 public
Jens Geyer3b686532021-07-01 23:04:08 +0200140 constructor Create(const trans : ITransport); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100141 destructor Destroy; override;
142
Jens Geyerfad7fd32019-11-09 23:24:52 +0100143 strict private
Jens Geyerf0e63312015-03-01 18:47:49 +0100144 procedure WriteByteDirect( const b : Byte); overload;
145
146 // Writes a byte without any possibility of all that field header nonsense.
147 procedure WriteByteDirect( const n : Integer); overload;
148
149 // Write an i32 as a varint. Results in 1-5 bytes on the wire.
150 // TODO: make a permanent buffer like WriteVarint64?
151 procedure WriteVarint32( n : Cardinal);
152
Jens Geyerfad7fd32019-11-09 23:24:52 +0100153 strict private
Jens Geyerf0e63312015-03-01 18:47:49 +0100154 // The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
155 // of the type header. This is used specifically in the boolean field case.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200156 procedure WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
Jens Geyerf0e63312015-03-01 18:47:49 +0100157
158 public
Jens Geyer17c3ad92017-09-05 20:31:27 +0200159 procedure WriteMessageBegin( const msg: TThriftMessage); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100160 procedure WriteMessageEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200161 procedure WriteStructBegin( const struc: TThriftStruct); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100162 procedure WriteStructEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200163 procedure WriteFieldBegin( const field: TThriftField); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100164 procedure WriteFieldEnd; override;
165 procedure WriteFieldStop; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200166 procedure WriteMapBegin( const map: TThriftMap); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100167 procedure WriteMapEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200168 procedure WriteListBegin( const list: TThriftList); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100169 procedure WriteListEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200170 procedure WriteSetBegin( const set_: TThriftSet ); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100171 procedure WriteSetEnd(); override;
172 procedure WriteBool( b: Boolean); override;
173 procedure WriteByte( b: ShortInt); override;
174 procedure WriteI16( i16: SmallInt); override;
175 procedure WriteI32( i32: Integer); override;
176 procedure WriteI64( const i64: Int64); override;
177 procedure WriteDouble( const dub: Double); override;
178 procedure WriteBinary( const b: TBytes); overload; override;
Jens Geyer62445c12022-06-29 00:00:00 +0200179 procedure WriteUuid( const uuid: TGuid); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100180
Jens Geyer41f47af2019-11-09 23:24:52 +0100181 private // unit visible stuff
Jens Geyerf0e63312015-03-01 18:47:49 +0100182 class function DoubleToInt64Bits( const db : Double) : Int64;
183 class function Int64BitsToDouble( const i64 : Int64) : Double;
184
185 // Abstract method for writing the start of lists and sets. List and sets on
186 // the wire differ only by the type indicator.
187 procedure WriteCollectionBegin( const elemType : TType; size : Integer);
188
189 procedure WriteVarint64( n : UInt64);
190
191 // Convert l into a zigzag long. This allows negative numbers to be
192 // represented compactly as a varint.
193 class function longToZigzag( const n : Int64) : UInt64;
194
195 // Convert n into a zigzag int. This allows negative numbers to be
196 // represented compactly as a varint.
197 class function intToZigZag( const n : Integer) : Cardinal;
198
199 //Convert a Int64 into little-endian bytes in buf starting at off and going until off+7.
Jens Geyerf726ae32021-06-04 11:17:26 +0200200 class procedure fixedLongToBytes( const n : Int64; var buf : TEightBytesArray); inline;
Jens Geyerf0e63312015-03-01 18:47:49 +0100201
Jens Geyer41f47af2019-11-09 23:24:52 +0100202 strict protected
203 function GetMinSerializedSize( const aType : TType) : Integer; override;
204 procedure Reset; override;
205
Jens Geyerf0e63312015-03-01 18:47:49 +0100206 public
Jens Geyer17c3ad92017-09-05 20:31:27 +0200207 function ReadMessageBegin: TThriftMessage; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100208 procedure ReadMessageEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200209 function ReadStructBegin: TThriftStruct; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100210 procedure ReadStructEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200211 function ReadFieldBegin: TThriftField; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100212 procedure ReadFieldEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200213 function ReadMapBegin: TThriftMap; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100214 procedure ReadMapEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200215 function ReadListBegin: TThriftList; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100216 procedure ReadListEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200217 function ReadSetBegin: TThriftSet; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100218 procedure ReadSetEnd(); override;
219 function ReadBool: Boolean; override;
220 function ReadByte: ShortInt; override;
221 function ReadI16: SmallInt; override;
222 function ReadI32: Integer; override;
223 function ReadI64: Int64; override;
224 function ReadDouble:Double; override;
225 function ReadBinary: TBytes; overload; override;
Jens Geyer62445c12022-06-29 00:00:00 +0200226 function ReadUuid: TGuid; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100227
228 private
229 // Internal Reading methods
230
231 // Read an i32 from the wire as a varint. The MSB of each byte is set
232 // if there is another byte to follow. This can Read up to 5 bytes.
233 function ReadVarint32 : Cardinal;
234
235 // Read an i64 from the wire as a proper varint. The MSB of each byte is set
236 // if there is another byte to follow. This can Read up to 10 bytes.
237 function ReadVarint64 : UInt64;
238
239
240 // encoding helpers
241
242 // Convert from zigzag Integer to Integer.
243 class function zigzagToInt( const n : Cardinal ) : Integer;
244
245 // Convert from zigzag Int64 to Int64.
246 class function zigzagToLong( const n : UInt64) : Int64;
247
248 // Note that it's important that the mask bytes are Int64 literals,
249 // otherwise they'll default to ints, and when you shift an Integer left 56 bits,
250 // you just get a messed up Integer.
Jens Geyerf726ae32021-06-04 11:17:26 +0200251 class function bytesToLong( const bytes : TEightBytesArray) : Int64; inline;
Jens Geyerf0e63312015-03-01 18:47:49 +0100252
253 // type testing and converting
254 class function isBoolType( const b : byte) : Boolean;
255
256 // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
257 class function getTType( const type_ : byte) : TType;
258
259 // Given a TType value, find the appropriate TCompactProtocol.Types constant.
260 class function getCompactType( const ttype : TType) : Byte;
261 end;
262
263
264implementation
265
266
267
268//--- TCompactProtocolImpl.TFactory ----------------------------------------
269
270
271function TCompactProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
272begin
273 result := TCompactProtocolImpl.Create( trans);
274end;
275
276
277//--- TCompactProtocolImpl -------------------------------------------------
278
279
Jens Geyera019cda2019-11-09 23:24:52 +0100280constructor TCompactProtocolImpl.Create( const trans : ITransport);
Jens Geyerf0e63312015-03-01 18:47:49 +0100281begin
282 inherited Create( trans);
283
284 lastFieldId_ := 0;
285 lastField_ := TStack<Integer>.Create;
286
Jens Geyer17c3ad92017-09-05 20:31:27 +0200287 Init( booleanField_, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100288 boolValue_ := unused;
289end;
290
291
292destructor TCompactProtocolImpl.Destroy;
293begin
294 try
295 FreeAndNil( lastField_);
296 finally
297 inherited Destroy;
298 end;
299end;
300
301
302
303procedure TCompactProtocolImpl.Reset;
304begin
Jens Geyer41f47af2019-11-09 23:24:52 +0100305 inherited Reset;
Jens Geyerf0e63312015-03-01 18:47:49 +0100306 lastField_.Clear();
307 lastFieldId_ := 0;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200308 Init( booleanField_, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100309 boolValue_ := unused;
310end;
311
312
313// Writes a byte without any possibility of all that field header nonsense.
314// Used internally by other writing methods that know they need to Write a byte.
315procedure TCompactProtocolImpl.WriteByteDirect( const b : Byte);
Jens Geyerf0e63312015-03-01 18:47:49 +0100316begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200317 Transport.Write( @b, SizeOf(b));
Jens Geyerf0e63312015-03-01 18:47:49 +0100318end;
319
320
321// Writes a byte without any possibility of all that field header nonsense.
322procedure TCompactProtocolImpl.WriteByteDirect( const n : Integer);
323begin
324 WriteByteDirect( Byte(n));
325end;
326
327
328// Write an i32 as a varint. Results in 1-5 bytes on the wire.
329procedure TCompactProtocolImpl.WriteVarint32( n : Cardinal);
Jens Geyerf726ae32021-06-04 11:17:26 +0200330var idx : Integer;
331 i32buf : packed array[0..4] of Byte;
Jens Geyerf0e63312015-03-01 18:47:49 +0100332begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100333 idx := 0;
334 while TRUE do begin
335 ASSERT( idx < Length(i32buf));
336
337 // last part?
338 if ((n and not $7F) = 0) then begin
339 i32buf[idx] := Byte(n);
340 Inc(idx);
341 Break;
342 end;
343
344 i32buf[idx] := Byte((n and $7F) or $80);
345 Inc(idx);
346 n := n shr 7;
347 end;
348
Jens Geyerf726ae32021-06-04 11:17:26 +0200349 Transport.Write( @i32buf[0], 0, idx);
Jens Geyerf0e63312015-03-01 18:47:49 +0100350end;
351
352
353// Write a message header to the wire. Compact Protocol messages contain the
354// protocol version so we can migrate forwards in the future if need be.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200355procedure TCompactProtocolImpl.WriteMessageBegin( const msg: TThriftMessage);
Jens Geyerf0e63312015-03-01 18:47:49 +0100356var versionAndType : Byte;
357begin
358 Reset;
359
360 versionAndType := Byte( VERSION and VERSION_MASK)
361 or Byte( (Cardinal(msg.Type_) shl TYPE_SHIFT_AMOUNT) and TYPE_MASK);
362
363 WriteByteDirect( PROTOCOL_ID);
364 WriteByteDirect( versionAndType);
365 WriteVarint32( Cardinal(msg.SeqID));
366 WriteString( msg.Name);
367end;
368
369
370// Write a struct begin. This doesn't actually put anything on the wire. We use it as an
371// opportunity to put special placeholder markers on the field stack so we can get the
372// field id deltas correct.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200373procedure TCompactProtocolImpl.WriteStructBegin( const struc: TThriftStruct);
Jens Geyerf0e63312015-03-01 18:47:49 +0100374begin
375 lastField_.Push(lastFieldId_);
376 lastFieldId_ := 0;
377end;
378
379
380// Write a struct end. This doesn't actually put anything on the wire. We use this as an
381// opportunity to pop the last field from the current struct off of the field stack.
382procedure TCompactProtocolImpl.WriteStructEnd;
383begin
384 lastFieldId_ := lastField_.Pop();
385end;
386
387
388// Write a field header containing the field id and field type. If the difference between the
389// current field id and the last one is small (< 15), then the field id will be encoded in
390// the 4 MSB as a delta. Otherwise, the field id will follow the type header as a zigzag varint.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200391procedure TCompactProtocolImpl.WriteFieldBegin( const field: TThriftField);
Jens Geyerf0e63312015-03-01 18:47:49 +0100392begin
393 case field.Type_ of
394 TType.Bool_ : booleanField_ := field; // we want to possibly include the value, so we'll wait.
395 else
396 WriteFieldBeginInternal(field, $FF);
397 end;
398end;
399
400
401// The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
402// of the type header. This is used specifically in the boolean field case.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200403procedure TCompactProtocolImpl.WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
Jens Geyerf0e63312015-03-01 18:47:49 +0100404var typeToWrite : Byte;
405begin
406 // if there's a type override, use that.
407 if typeOverride = $FF
408 then typeToWrite := getCompactType( field.Type_)
409 else typeToWrite := typeOverride;
410
411 // check if we can use delta encoding for the field id
412 if (field.ID > lastFieldId_) and ((field.ID - lastFieldId_) <= 15)
413 then begin
414 // Write them together
415 WriteByteDirect( ((field.ID - lastFieldId_) shl 4) or typeToWrite);
416 end
417 else begin
418 // Write them separate
419 WriteByteDirect( typeToWrite);
420 WriteI16( field.ID);
421 end;
422
423 lastFieldId_ := field.ID;
424end;
425
426
427// Write the STOP symbol so we know there are no more fields in this struct.
428procedure TCompactProtocolImpl.WriteFieldStop;
429begin
430 WriteByteDirect( Byte( Types.STOP));
431end;
432
433
434// Write a map header. If the map is empty, omit the key and value type
435// headers, as we don't need any additional information to skip it.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200436procedure TCompactProtocolImpl.WriteMapBegin( const map: TThriftMap);
Jens Geyerf0e63312015-03-01 18:47:49 +0100437var key, val : Byte;
438begin
439 if (map.Count = 0)
440 then WriteByteDirect( 0)
441 else begin
442 WriteVarint32( Cardinal( map.Count));
443 key := getCompactType(map.KeyType);
444 val := getCompactType(map.ValueType);
445 WriteByteDirect( (key shl 4) or val);
446 end;
447end;
448
449
450// Write a list header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200451procedure TCompactProtocolImpl.WriteListBegin( const list: TThriftList);
Jens Geyerf0e63312015-03-01 18:47:49 +0100452begin
453 WriteCollectionBegin( list.ElementType, list.Count);
454end;
455
456
457// Write a set header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200458procedure TCompactProtocolImpl.WriteSetBegin( const set_: TThriftSet );
Jens Geyerf0e63312015-03-01 18:47:49 +0100459begin
460 WriteCollectionBegin( set_.ElementType, set_.Count);
461end;
462
463
464// Write a boolean value. Potentially, this could be a boolean field, in
465// which case the field header info isn't written yet. If so, decide what the
466// right type header is for the value and then Write the field header.
467// Otherwise, Write a single byte.
468procedure TCompactProtocolImpl.WriteBool( b: Boolean);
469var bt : Types;
470begin
471 if b
472 then bt := Types.BOOLEAN_TRUE
473 else bt := Types.BOOLEAN_FALSE;
474
Jens Geyer17c3ad92017-09-05 20:31:27 +0200475 if booleanField_.Type_ = TType.Bool_ then begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100476 // we haven't written the field header yet
477 WriteFieldBeginInternal( booleanField_, Byte(bt));
Jens Geyer17c3ad92017-09-05 20:31:27 +0200478 booleanField_.Type_ := TType.Stop;
Jens Geyerf0e63312015-03-01 18:47:49 +0100479 end
480 else begin
481 // we're not part of a field, so just Write the value.
482 WriteByteDirect( Byte(bt));
483 end;
484end;
485
486
487// Write a byte. Nothing to see here!
488procedure TCompactProtocolImpl.WriteByte( b: ShortInt);
489begin
490 WriteByteDirect( Byte(b));
491end;
492
493
494// Write an I16 as a zigzag varint.
495procedure TCompactProtocolImpl.WriteI16( i16: SmallInt);
496begin
497 WriteVarint32( intToZigZag( i16));
498end;
499
500
501// Write an i32 as a zigzag varint.
502procedure TCompactProtocolImpl.WriteI32( i32: Integer);
503begin
504 WriteVarint32( intToZigZag( i32));
505end;
506
507
508// Write an i64 as a zigzag varint.
509procedure TCompactProtocolImpl.WriteI64( const i64: Int64);
510begin
511 WriteVarint64( longToZigzag( i64));
512end;
513
514
515class function TCompactProtocolImpl.DoubleToInt64Bits( const db : Double) : Int64;
516begin
517 ASSERT( SizeOf(db) = SizeOf(result));
518 Move( db, result, SizeOf(result));
519end;
520
521
522class function TCompactProtocolImpl.Int64BitsToDouble( const i64 : Int64) : Double;
523begin
524 ASSERT( SizeOf(i64) = SizeOf(result));
525 Move( i64, result, SizeOf(result));
526end;
527
528
529// Write a double to the wire as 8 bytes.
530procedure TCompactProtocolImpl.WriteDouble( const dub: Double);
Jens Geyerf726ae32021-06-04 11:17:26 +0200531var data : TEightBytesArray;
Jens Geyerf0e63312015-03-01 18:47:49 +0100532begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100533 fixedLongToBytes( DoubleToInt64Bits(dub), data);
Jens Geyerf726ae32021-06-04 11:17:26 +0200534 Transport.Write( @data[0], 0, SizeOf(data));
Jens Geyerf0e63312015-03-01 18:47:49 +0100535end;
536
537
538// Write a byte array, using a varint for the size.
539procedure TCompactProtocolImpl.WriteBinary( const b: TBytes);
540begin
541 WriteVarint32( Cardinal(Length(b)));
542 Transport.Write( b);
543end;
544
Jens Geyer62445c12022-06-29 00:00:00 +0200545procedure TCompactProtocolImpl.WriteUuid( const uuid: TGuid);
546var network : TGuid; // in network order (Big Endian)
547begin
548 ASSERT( SizeOf(uuid) = 16);
549 network := uuid.SwapByteOrder;
550 Transport.Write( @network, 0, SizeOf(network));
551end;
552
Jens Geyerf0e63312015-03-01 18:47:49 +0100553procedure TCompactProtocolImpl.WriteMessageEnd;
554begin
555 // nothing to do
556end;
557
558
559procedure TCompactProtocolImpl.WriteMapEnd;
560begin
561 // nothing to do
562end;
563
564
565procedure TCompactProtocolImpl.WriteListEnd;
566begin
567 // nothing to do
568end;
569
570
571procedure TCompactProtocolImpl.WriteSetEnd;
572begin
573 // nothing to do
574end;
575
576
577procedure TCompactProtocolImpl.WriteFieldEnd;
578begin
579 // nothing to do
580end;
581
582
583// Abstract method for writing the start of lists and sets. List and sets on
584// the wire differ only by the type indicator.
585procedure TCompactProtocolImpl.WriteCollectionBegin( const elemType : TType; size : Integer);
586begin
587 if size <= 14
588 then WriteByteDirect( (size shl 4) or getCompactType(elemType))
589 else begin
590 WriteByteDirect( $F0 or getCompactType(elemType));
591 WriteVarint32( Cardinal(size));
592 end;
593end;
594
595
596// Write an i64 as a varint. Results in 1-10 bytes on the wire.
597procedure TCompactProtocolImpl.WriteVarint64( n : UInt64);
Jens Geyerf726ae32021-06-04 11:17:26 +0200598var idx : Integer;
599 varint64out : packed array[0..9] of Byte;
Jens Geyerf0e63312015-03-01 18:47:49 +0100600begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100601 idx := 0;
602 while TRUE do begin
603 ASSERT( idx < Length(varint64out));
604
605 // last one?
606 if (n and not UInt64($7F)) = 0 then begin
607 varint64out[idx] := Byte(n);
608 Inc(idx);
609 Break;
610 end;
611
612 varint64out[idx] := Byte((n and $7F) or $80);
613 Inc(idx);
614 n := n shr 7;
615 end;
616
Jens Geyerf726ae32021-06-04 11:17:26 +0200617 Transport.Write( @varint64out[0], 0, idx);
Jens Geyerf0e63312015-03-01 18:47:49 +0100618end;
619
620
621// Convert l into a zigzag Int64. This allows negative numbers to be
622// represented compactly as a varint.
623class function TCompactProtocolImpl.longToZigzag( const n : Int64) : UInt64;
624begin
625 // there is no arithmetic right shift in Delphi
626 if n >= 0
627 then result := UInt64(n shl 1)
628 else result := UInt64(n shl 1) xor $FFFFFFFFFFFFFFFF;
629end;
630
631
632// Convert n into a zigzag Integer. This allows negative numbers to be
633// represented compactly as a varint.
634class function TCompactProtocolImpl.intToZigZag( const n : Integer) : Cardinal;
635begin
636 // there is no arithmetic right shift in Delphi
637 if n >= 0
638 then result := Cardinal(n shl 1)
639 else result := Cardinal(n shl 1) xor $FFFFFFFF;
640end;
641
642
643// Convert a Int64 into 8 little-endian bytes in buf
Jens Geyerf726ae32021-06-04 11:17:26 +0200644class procedure TCompactProtocolImpl.fixedLongToBytes( const n : Int64; var buf : TEightBytesArray);
Jens Geyerf0e63312015-03-01 18:47:49 +0100645begin
Jens Geyerf726ae32021-06-04 11:17:26 +0200646 ASSERT( Length(buf) >= 8);
Jens Geyerf0e63312015-03-01 18:47:49 +0100647 buf[0] := Byte( n and $FF);
648 buf[1] := Byte((n shr 8) and $FF);
649 buf[2] := Byte((n shr 16) and $FF);
650 buf[3] := Byte((n shr 24) and $FF);
651 buf[4] := Byte((n shr 32) and $FF);
652 buf[5] := Byte((n shr 40) and $FF);
653 buf[6] := Byte((n shr 48) and $FF);
654 buf[7] := Byte((n shr 56) and $FF);
655end;
656
657
658
659// Read a message header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200660function TCompactProtocolImpl.ReadMessageBegin : TThriftMessage;
Jens Geyerf0e63312015-03-01 18:47:49 +0100661var protocolId, versionAndType, version, type_ : Byte;
662 seqid : Integer;
663 msgNm : String;
664begin
665 Reset;
666
667 protocolId := Byte( ReadByte);
668 if (protocolId <> PROTOCOL_ID)
Jens Geyere0e32402016-04-20 21:50:48 +0200669 then raise TProtocolExceptionBadVersion.Create( 'Expected protocol id ' + IntToHex(PROTOCOL_ID,2)
670 + ' but got ' + IntToHex(protocolId,2));
Jens Geyerf0e63312015-03-01 18:47:49 +0100671
672 versionAndType := Byte( ReadByte);
673 version := Byte( versionAndType and VERSION_MASK);
674 if (version <> VERSION)
Jens Geyere0e32402016-04-20 21:50:48 +0200675 then raise TProtocolExceptionBadVersion.Create( 'Expected version ' +IntToStr(VERSION)
676 + ' but got ' + IntToStr(version));
Jens Geyerf0e63312015-03-01 18:47:49 +0100677
678 type_ := Byte( (versionAndType shr TYPE_SHIFT_AMOUNT) and TYPE_BITS);
679 seqid := Integer( ReadVarint32);
680 msgNm := ReadString;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200681 Init( result, msgNm, TMessageType(type_), seqid);
Jens Geyerf0e63312015-03-01 18:47:49 +0100682end;
683
684
685// Read a struct begin. There's nothing on the wire for this, but it is our
686// opportunity to push a new struct begin marker onto the field stack.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200687function TCompactProtocolImpl.ReadStructBegin: TThriftStruct;
Jens Geyerf0e63312015-03-01 18:47:49 +0100688begin
689 lastField_.Push( lastFieldId_);
690 lastFieldId_ := 0;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200691 Init( result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100692end;
693
694
695// Doesn't actually consume any wire data, just removes the last field for
696// this struct from the field stack.
697procedure TCompactProtocolImpl.ReadStructEnd;
698begin
699 // consume the last field we Read off the wire.
700 lastFieldId_ := lastField_.Pop();
701end;
702
703
704// Read a field header off the wire.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200705function TCompactProtocolImpl.ReadFieldBegin: TThriftField;
Jens Geyerf0e63312015-03-01 18:47:49 +0100706var type_ : Byte;
Jens Geyera715f702019-08-28 22:56:13 +0200707 modifier : ShortInt;
708 fieldId : SmallInt;
Jens Geyerf0e63312015-03-01 18:47:49 +0100709begin
710 type_ := Byte( ReadByte);
711
712 // if it's a stop, then we can return immediately, as the struct is over.
713 if type_ = Byte(Types.STOP) then begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200714 Init( result, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100715 Exit;
716 end;
717
718 // mask off the 4 MSB of the type header. it could contain a field id delta.
719 modifier := ShortInt( (type_ and $F0) shr 4);
720 if (modifier = 0)
721 then fieldId := ReadI16 // not a delta. look ahead for the zigzag varint field id.
Jens Geyera715f702019-08-28 22:56:13 +0200722 else fieldId := SmallInt( lastFieldId_ + modifier); // add the delta to the last Read field id.
Jens Geyerf0e63312015-03-01 18:47:49 +0100723
Jens Geyer17c3ad92017-09-05 20:31:27 +0200724 Init( result, '', getTType(Byte(type_ and $0F)), fieldId);
Jens Geyerf0e63312015-03-01 18:47:49 +0100725
726 // if this happens to be a boolean field, the value is encoded in the type
727 // save the boolean value in a special instance variable.
728 if isBoolType(type_) then begin
729 if Byte(type_ and $0F) = Byte(Types.BOOLEAN_TRUE)
730 then boolValue_ := bool_true
731 else boolValue_ := bool_false;
732 end;
733
734 // push the new field onto the field stack so we can keep the deltas going.
735 lastFieldId_ := result.ID;
736end;
737
738
739// Read a map header off the wire. If the size is zero, skip Reading the key
740// and value type. This means that 0-length maps will yield TMaps without the
741// "correct" types.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200742function TCompactProtocolImpl.ReadMapBegin: TThriftMap;
Jens Geyerf0e63312015-03-01 18:47:49 +0100743var size : Integer;
744 keyAndValueType : Byte;
745 key, val : TType;
746begin
747 size := Integer( ReadVarint32);
748 if size = 0
749 then keyAndValueType := 0
750 else keyAndValueType := Byte( ReadByte);
751
752 key := getTType( Byte( keyAndValueType shr 4));
753 val := getTType( Byte( keyAndValueType and $F));
Jens Geyer17c3ad92017-09-05 20:31:27 +0200754 Init( result, key, val, size);
Jens Geyerf0e63312015-03-01 18:47:49 +0100755 ASSERT( (result.KeyType = key) and (result.ValueType = val));
Jens Geyer41f47af2019-11-09 23:24:52 +0100756 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100757end;
758
759
760// Read a list header off the wire. If the list size is 0-14, the size will
761// be packed into the element type header. If it's a longer list, the 4 MSB
762// of the element type header will be $F, and a varint will follow with the
763// true size.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200764function TCompactProtocolImpl.ReadListBegin: TThriftList;
Jens Geyerf0e63312015-03-01 18:47:49 +0100765var size_and_type : Byte;
766 size : Integer;
767 type_ : TType;
768begin
769 size_and_type := Byte( ReadByte);
770
771 size := (size_and_type shr 4) and $0F;
772 if (size = 15)
773 then size := Integer( ReadVarint32);
774
775 type_ := getTType( size_and_type);
Jens Geyer17c3ad92017-09-05 20:31:27 +0200776 Init( result, type_, size);
Jens Geyer41f47af2019-11-09 23:24:52 +0100777 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100778end;
779
780
781// Read a set header off the wire. If the set size is 0-14, the size will
782// be packed into the element type header. If it's a longer set, the 4 MSB
783// of the element type header will be $F, and a varint will follow with the
784// true size.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200785function TCompactProtocolImpl.ReadSetBegin: TThriftSet;
Jens Geyerf0e63312015-03-01 18:47:49 +0100786var size_and_type : Byte;
787 size : Integer;
788 type_ : TType;
789begin
790 size_and_type := Byte( ReadByte);
791
792 size := (size_and_type shr 4) and $0F;
793 if (size = 15)
794 then size := Integer( ReadVarint32);
795
796 type_ := getTType( size_and_type);
Jens Geyer17c3ad92017-09-05 20:31:27 +0200797 Init( result, type_, size);
Jens Geyer41f47af2019-11-09 23:24:52 +0100798 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100799end;
800
801
802// Read a boolean off the wire. If this is a boolean field, the value should
803// already have been Read during ReadFieldBegin, so we'll just consume the
804// pre-stored value. Otherwise, Read a byte.
805function TCompactProtocolImpl.ReadBool: Boolean;
806begin
807 if boolValue_ <> unused then begin
808 result := (boolValue_ = bool_true);
809 boolValue_ := unused;
810 Exit;
811 end;
812
813 result := (Byte(ReadByte) = Byte(Types.BOOLEAN_TRUE));
814end;
815
816
817// Read a single byte off the wire. Nothing interesting here.
818function TCompactProtocolImpl.ReadByte: ShortInt;
Jens Geyerf0e63312015-03-01 18:47:49 +0100819begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200820 Transport.ReadAll( @result, SizeOf(result), 0, 1);
Jens Geyerf0e63312015-03-01 18:47:49 +0100821end;
822
823
824// Read an i16 from the wire as a zigzag varint.
825function TCompactProtocolImpl.ReadI16: SmallInt;
826begin
827 result := SmallInt( zigzagToInt( ReadVarint32));
828end;
829
830
831// Read an i32 from the wire as a zigzag varint.
832function TCompactProtocolImpl.ReadI32: Integer;
833begin
834 result := zigzagToInt( ReadVarint32);
835end;
836
837
838// Read an i64 from the wire as a zigzag varint.
839function TCompactProtocolImpl.ReadI64: Int64;
840begin
841 result := zigzagToLong( ReadVarint64);
842end;
843
844
845// No magic here - just Read a double off the wire.
Jens Geyerf726ae32021-06-04 11:17:26 +0200846function TCompactProtocolImpl.ReadDouble : Double;
847var longBits : TEightBytesArray;
Jens Geyerf0e63312015-03-01 18:47:49 +0100848begin
Jens Geyerf726ae32021-06-04 11:17:26 +0200849 ASSERT( SizeOf(longBits) = SizeOf(result));
850 Transport.ReadAll( @longBits[0], SizeOf(longBits), 0, SizeOf(longBits));
Jens Geyerf0e63312015-03-01 18:47:49 +0100851 result := Int64BitsToDouble( bytesToLong( longBits));
852end;
853
854
855// Read a byte[] from the wire.
856function TCompactProtocolImpl.ReadBinary: TBytes;
857var length : Integer;
858begin
859 length := Integer( ReadVarint32);
Jens Geyer41f47af2019-11-09 23:24:52 +0100860 FTrans.CheckReadBytesAvailable(length);
Jens Geyerf0e63312015-03-01 18:47:49 +0100861 SetLength( result, length);
862 if (length > 0)
863 then Transport.ReadAll( result, 0, length);
864end;
865
Jens Geyer62445c12022-06-29 00:00:00 +0200866function TCompactProtocolImpl.ReadUuid: TGuid;
867var network : TGuid; // in network order (Big Endian)
868begin
869 ASSERT( SizeOf(result) = 16);
870 FTrans.ReadAll( @network, SizeOf(network), 0, SizeOf(network));
871 result := network.SwapByteOrder;
872end;
873
Jens Geyerf0e63312015-03-01 18:47:49 +0100874
875procedure TCompactProtocolImpl.ReadMessageEnd;
876begin
877 // nothing to do
878end;
879
880
881procedure TCompactProtocolImpl.ReadFieldEnd;
882begin
883 // nothing to do
884end;
885
886
887procedure TCompactProtocolImpl.ReadMapEnd;
888begin
889 // nothing to do
890end;
891
892
893procedure TCompactProtocolImpl.ReadListEnd;
894begin
895 // nothing to do
896end;
897
898
899procedure TCompactProtocolImpl.ReadSetEnd;
900begin
901 // nothing to do
902end;
903
904
905
906// Read an i32 from the wire as a varint. The MSB of each byte is set
907// if there is another byte to follow. This can Read up to 5 bytes.
908function TCompactProtocolImpl.ReadVarint32 : Cardinal;
909var shift : Integer;
910 b : Byte;
911begin
912 result := 0;
913 shift := 0;
914 while TRUE do begin
915 b := Byte( ReadByte);
916 result := result or (Cardinal(b and $7F) shl shift);
917 if ((b and $80) <> $80)
918 then Break;
919 Inc( shift, 7);
920 end;
921end;
922
923
924// Read an i64 from the wire as a proper varint. The MSB of each byte is set
925// if there is another byte to follow. This can Read up to 10 bytes.
926function TCompactProtocolImpl.ReadVarint64 : UInt64;
927var shift : Integer;
928 b : Byte;
929begin
930 result := 0;
931 shift := 0;
932 while TRUE do begin
933 b := Byte( ReadByte);
934 result := result or (UInt64(b and $7F) shl shift);
935 if ((b and $80) <> $80)
936 then Break;
937 Inc( shift, 7);
938 end;
939end;
940
941
942// Convert from zigzag Integer to Integer.
943class function TCompactProtocolImpl.zigzagToInt( const n : Cardinal ) : Integer;
944begin
945 result := Integer(n shr 1) xor (-Integer(n and 1));
946end;
947
948
949// Convert from zigzag Int64 to Int64.
950class function TCompactProtocolImpl.zigzagToLong( const n : UInt64) : Int64;
951begin
952 result := Int64(n shr 1) xor (-Int64(n and 1));
953end;
954
955
956// Note that it's important that the mask bytes are Int64 literals,
957// otherwise they'll default to ints, and when you shift an Integer left 56 bits,
958// you just get a messed up Integer.
Jens Geyerf726ae32021-06-04 11:17:26 +0200959class function TCompactProtocolImpl.bytesToLong( const bytes : TEightBytesArray) : Int64;
Jens Geyerf0e63312015-03-01 18:47:49 +0100960begin
961 ASSERT( Length(bytes) >= 8);
962 result := (Int64(bytes[7] and $FF) shl 56) or
963 (Int64(bytes[6] and $FF) shl 48) or
964 (Int64(bytes[5] and $FF) shl 40) or
965 (Int64(bytes[4] and $FF) shl 32) or
966 (Int64(bytes[3] and $FF) shl 24) or
967 (Int64(bytes[2] and $FF) shl 16) or
968 (Int64(bytes[1] and $FF) shl 8) or
969 (Int64(bytes[0] and $FF));
970end;
971
972
973class function TCompactProtocolImpl.isBoolType( const b : byte) : Boolean;
974var lowerNibble : Byte;
975begin
976 lowerNibble := b and $0f;
977 result := (Types(lowerNibble) in [Types.BOOLEAN_TRUE, Types.BOOLEAN_FALSE]);
978end;
979
980
981// Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
982class function TCompactProtocolImpl.getTType( const type_ : byte) : TType;
983var tct : Types;
984begin
985 tct := Types( type_ and $0F);
986 if tct in [Low(Types)..High(Types)]
987 then result := tcompactTypeToType[tct]
Jens Geyere0e32402016-04-20 21:50:48 +0200988 else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(tct)));
Jens Geyerf0e63312015-03-01 18:47:49 +0100989end;
990
991
992// Given a TType value, find the appropriate TCompactProtocol.Types constant.
993class function TCompactProtocolImpl.getCompactType( const ttype : TType) : Byte;
994begin
995 if ttype in VALID_TTYPES
996 then result := Byte( ttypeToCompactType[ttype])
Jens Geyere0e32402016-04-20 21:50:48 +0200997 else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(ttype)));
Jens Geyerf0e63312015-03-01 18:47:49 +0100998end;
999
1000
Jens Geyer41f47af2019-11-09 23:24:52 +01001001function TCompactProtocolImpl.GetMinSerializedSize( const aType : TType) : Integer;
1002// Return the minimum number of bytes a type will consume on the wire
1003begin
1004 case aType of
1005 TType.Stop: result := 0;
1006 TType.Void: result := 0;
1007 TType.Bool_: result := SizeOf(Byte);
1008 TType.Byte_: result := SizeOf(Byte);
1009 TType.Double_: result := 8; // uses fixedLongToBytes() which always writes 8 bytes
1010 TType.I16: result := SizeOf(Byte);
1011 TType.I32: result := SizeOf(Byte);
1012 TType.I64: result := SizeOf(Byte);
1013 TType.String_: result := SizeOf(Byte); // string length
1014 TType.Struct: result := 0; // empty struct
1015 TType.Map: result := SizeOf(Byte); // element count
1016 TType.Set_: result := SizeOf(Byte); // element count
1017 TType.List: result := SizeOf(Byte); // element count
Jens Geyer62445c12022-06-29 00:00:00 +02001018 TType.Uuid: result := SizeOf(TGuid);
Jens Geyer41f47af2019-11-09 23:24:52 +01001019 else
1020 raise TTransportExceptionBadArgs.Create('Unhandled type code');
1021 end;
1022end;
1023
1024
1025
1026
1027
Jens Geyerf0e63312015-03-01 18:47:49 +01001028//--- unit tests -------------------------------------------
1029
1030{$IFDEF Debug}
1031procedure TestDoubleToInt64Bits;
1032
1033 procedure TestPair( const a : Double; const b : Int64);
1034 begin
1035 ASSERT( TCompactProtocolImpl.DoubleToInt64Bits(a) = b);
1036 ASSERT( TCompactProtocolImpl.Int64BitsToDouble(b) = a);
1037 end;
1038
1039begin
1040 TestPair( 1.0000000000000000E+000, Int64($3FF0000000000000));
1041 TestPair( 1.5000000000000000E+001, Int64($402E000000000000));
1042 TestPair( 2.5500000000000000E+002, Int64($406FE00000000000));
1043 TestPair( 4.2949672950000000E+009, Int64($41EFFFFFFFE00000));
1044 TestPair( 3.9062500000000000E-003, Int64($3F70000000000000));
1045 TestPair( 2.3283064365386963E-010, Int64($3DF0000000000000));
1046 TestPair( 1.2345678901230000E-300, Int64($01AA74FE1C1E7E45));
1047 TestPair( 1.2345678901234500E-150, Int64($20D02A36586DB4BB));
1048 TestPair( 1.2345678901234565E+000, Int64($3FF3C0CA428C59FA));
1049 TestPair( 1.2345678901234567E+000, Int64($3FF3C0CA428C59FB));
1050 TestPair( 1.2345678901234569E+000, Int64($3FF3C0CA428C59FC));
1051 TestPair( 1.2345678901234569E+150, Int64($5F182344CD3CDF9F));
1052 TestPair( 1.2345678901234569E+300, Int64($7E3D7EE8BCBBD352));
1053 TestPair( -1.7976931348623157E+308, Int64($FFEFFFFFFFFFFFFF));
1054 TestPair( 1.7976931348623157E+308, Int64($7FEFFFFFFFFFFFFF));
1055 TestPair( 4.9406564584124654E-324, Int64($0000000000000001));
1056 TestPair( 0.0000000000000000E+000, Int64($0000000000000000));
1057 TestPair( 4.94065645841247E-324, Int64($0000000000000001));
1058 TestPair( 3.2378592100206092E-319, Int64($000000000000FFFF));
1059 TestPair( 1.3906711615669959E-309, Int64($0000FFFFFFFFFFFF));
1060 TestPair( NegInfinity, Int64($FFF0000000000000));
1061 TestPair( Infinity, Int64($7FF0000000000000));
1062
1063 // NaN is special
1064 ASSERT( TCompactProtocolImpl.DoubleToInt64Bits( NaN) = Int64($FFF8000000000000));
1065 ASSERT( IsNan( TCompactProtocolImpl.Int64BitsToDouble( Int64($FFF8000000000000))));
1066end;
1067{$ENDIF}
1068
1069
1070{$IFDEF Debug}
1071procedure TestZigZag;
1072
1073 procedure Test32( const test : Integer);
1074 var zz : Cardinal;
1075 begin
1076 zz := TCompactProtocolImpl.intToZigZag(test);
1077 ASSERT( TCompactProtocolImpl.zigzagToInt(zz) = test, IntToStr(test));
1078 end;
1079
1080 procedure Test64( const test : Int64);
1081 var zz : UInt64;
1082 begin
1083 zz := TCompactProtocolImpl.longToZigzag(test);
1084 ASSERT( TCompactProtocolImpl.zigzagToLong(zz) = test, IntToStr(test));
1085 end;
1086
1087var i : Integer;
1088begin
1089 // protobuf testcases
Jens Geyerd6834402015-03-07 13:16:34 +01001090 ASSERT( TCompactProtocolImpl.intToZigZag(0) = 0, 'pb #1 to ZigZag');
1091 ASSERT( TCompactProtocolImpl.intToZigZag(-1) = 1, 'pb #2 to ZigZag');
1092 ASSERT( TCompactProtocolImpl.intToZigZag(1) = 2, 'pb #3 to ZigZag');
1093 ASSERT( TCompactProtocolImpl.intToZigZag(-2) = 3, 'pb #4 to ZigZag');
1094 ASSERT( TCompactProtocolImpl.intToZigZag(+2147483647) = 4294967294, 'pb #5 to ZigZag');
1095 ASSERT( TCompactProtocolImpl.intToZigZag(-2147483648) = 4294967295, 'pb #6 to ZigZag');
1096
1097 // protobuf testcases
1098 ASSERT( TCompactProtocolImpl.zigzagToInt(0) = 0, 'pb #1 from ZigZag');
1099 ASSERT( TCompactProtocolImpl.zigzagToInt(1) = -1, 'pb #2 from ZigZag');
1100 ASSERT( TCompactProtocolImpl.zigzagToInt(2) = 1, 'pb #3 from ZigZag');
1101 ASSERT( TCompactProtocolImpl.zigzagToInt(3) = -2, 'pb #4 from ZigZag');
1102 ASSERT( TCompactProtocolImpl.zigzagToInt(4294967294) = +2147483647, 'pb #5 from ZigZag');
1103 ASSERT( TCompactProtocolImpl.zigzagToInt(4294967295) = -2147483648, 'pb #6 from ZigZag');
Jens Geyerf0e63312015-03-01 18:47:49 +01001104
1105 // back and forth 32
1106 Test32( 0);
1107 for i := 0 to 30 do begin
1108 Test32( +(Integer(1) shl i));
1109 Test32( -(Integer(1) shl i));
1110 end;
1111 Test32( Integer($7FFFFFFF));
1112 Test32( Integer($80000000));
1113
1114 // back and forth 64
1115 Test64( 0);
1116 for i := 0 to 62 do begin
1117 Test64( +(Int64(1) shl i));
1118 Test64( -(Int64(1) shl i));
1119 end;
1120 Test64( Int64($7FFFFFFFFFFFFFFF));
1121 Test64( Int64($8000000000000000));
1122end;
1123{$ENDIF}
1124
1125
Jens Geyera6ea4442015-03-02 23:06:57 +01001126{$IFDEF Debug}
1127procedure TestLongBytes;
1128
1129 procedure Test( const test : Int64);
Jens Geyerf726ae32021-06-04 11:17:26 +02001130 var buf : TCompactProtocolImpl.TEightBytesArray;
Jens Geyera6ea4442015-03-02 23:06:57 +01001131 begin
1132 TCompactProtocolImpl.fixedLongToBytes( test, buf);
1133 ASSERT( TCompactProtocolImpl.bytesToLong( buf) = test, IntToStr(test));
1134 end;
1135
1136var i : Integer;
1137begin
1138 Test( 0);
1139 for i := 0 to 62 do begin
1140 Test( +(Int64(1) shl i));
1141 Test( -(Int64(1) shl i));
1142 end;
1143 Test( Int64($7FFFFFFFFFFFFFFF));
1144 Test( Int64($8000000000000000));
1145end;
1146{$ENDIF}
1147
1148
Jens Geyera9235802018-09-25 00:21:12 +02001149{$IFDEF Debug}
1150procedure UnitTest;
1151var w : WORD;
1152const FPU_CW_DENORMALIZED = $0002;
1153begin
1154 w := Get8087CW;
1155 try
1156 Set8087CW( w or FPU_CW_DENORMALIZED);
1157
1158 TestDoubleToInt64Bits;
1159 TestZigZag;
1160 TestLongBytes;
1161
1162 finally
1163 Set8087CW( w);
1164 end;
1165end;
1166{$ENDIF}
1167
1168
Jens Geyerf0e63312015-03-01 18:47:49 +01001169initialization
1170 {$IFDEF Debug}
Jens Geyera9235802018-09-25 00:21:12 +02001171 UnitTest;
Jens Geyerf0e63312015-03-01 18:47:49 +01001172 {$ENDIF}
1173
1174end.
1175