blob: 3a1dbfd0bd19e06d77dcf3183fa9cd65b15ec374 [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,
80 STRUCT = $0C
81 );
82
Jens Geyerf726ae32021-06-04 11:17:26 +020083 private type
84 TEightBytesArray = packed array[0..7] of Byte;
85
Jens Geyerfad7fd32019-11-09 23:24:52 +010086 strict private const
Jens Geyerf0e63312015-03-01 18:47:49 +010087 ttypeToCompactType : array[TType] of Types = (
88 Types.STOP, // Stop = 0,
89 Types(-1), // Void = 1,
90 Types.BOOLEAN_TRUE, // Bool_ = 2,
91 Types.BYTE_, // Byte_ = 3,
92 Types.DOUBLE_, // Double_ = 4,
93 Types(-5), // unused
94 Types.I16, // I16 = 6,
95 Types(-7), // unused
96 Types.I32, // I32 = 8,
97 Types(-9), // unused
98 Types.I64, // I64 = 10,
99 Types.BINARY, // String_ = 11,
100 Types.STRUCT, // Struct = 12,
101 Types.MAP, // Map = 13,
102 Types.SET_, // Set_ = 14,
103 Types.LIST // List = 15,
104 );
105
106 tcompactTypeToType : array[Types] of TType = (
107 TType.Stop, // STOP
108 TType.Bool_, // BOOLEAN_TRUE
109 TType.Bool_, // BOOLEAN_FALSE
110 TType.Byte_, // BYTE_
111 TType.I16, // I16
112 TType.I32, // I32
113 TType.I64, // I64
114 TType.Double_, // DOUBLE_
115 TType.String_, // BINARY
116 TType.List, // LIST
117 TType.Set_, // SET_
118 TType.Map, // MAP
119 TType.Struct // STRUCT
120 );
121
Jens Geyerfad7fd32019-11-09 23:24:52 +0100122 strict private
Jens Geyerf0e63312015-03-01 18:47:49 +0100123 // Used to keep track of the last field for the current and previous structs,
124 // so we can do the delta stuff.
125 lastField_ : TStack<Integer>;
126 lastFieldId_ : Integer;
127
128 // If we encounter a boolean field begin, save the TField here so it can
129 // have the value incorporated.
Jens Geyerfad7fd32019-11-09 23:24:52 +0100130 strict private booleanField_ : TThriftField;
Jens Geyerf0e63312015-03-01 18:47:49 +0100131
132 // If we Read a field header, and it's a boolean field, save the boolean
133 // value here so that ReadBool can use it.
Jens Geyerfad7fd32019-11-09 23:24:52 +0100134 strict private boolValue_ : ( unused, bool_true, bool_false);
Jens Geyerf0e63312015-03-01 18:47:49 +0100135
136 public
Jens Geyer3b686532021-07-01 23:04:08 +0200137 constructor Create(const trans : ITransport); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100138 destructor Destroy; override;
139
Jens Geyerfad7fd32019-11-09 23:24:52 +0100140 strict private
Jens Geyerf0e63312015-03-01 18:47:49 +0100141 procedure WriteByteDirect( const b : Byte); overload;
142
143 // Writes a byte without any possibility of all that field header nonsense.
144 procedure WriteByteDirect( const n : Integer); overload;
145
146 // Write an i32 as a varint. Results in 1-5 bytes on the wire.
147 // TODO: make a permanent buffer like WriteVarint64?
148 procedure WriteVarint32( n : Cardinal);
149
Jens Geyerfad7fd32019-11-09 23:24:52 +0100150 strict private
Jens Geyerf0e63312015-03-01 18:47:49 +0100151 // The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
152 // of the type header. This is used specifically in the boolean field case.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200153 procedure WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
Jens Geyerf0e63312015-03-01 18:47:49 +0100154
155 public
Jens Geyer17c3ad92017-09-05 20:31:27 +0200156 procedure WriteMessageBegin( const msg: TThriftMessage); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100157 procedure WriteMessageEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200158 procedure WriteStructBegin( const struc: TThriftStruct); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100159 procedure WriteStructEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200160 procedure WriteFieldBegin( const field: TThriftField); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100161 procedure WriteFieldEnd; override;
162 procedure WriteFieldStop; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200163 procedure WriteMapBegin( const map: TThriftMap); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100164 procedure WriteMapEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200165 procedure WriteListBegin( const list: TThriftList); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100166 procedure WriteListEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200167 procedure WriteSetBegin( const set_: TThriftSet ); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100168 procedure WriteSetEnd(); override;
169 procedure WriteBool( b: Boolean); override;
170 procedure WriteByte( b: ShortInt); override;
171 procedure WriteI16( i16: SmallInt); override;
172 procedure WriteI32( i32: Integer); override;
173 procedure WriteI64( const i64: Int64); override;
174 procedure WriteDouble( const dub: Double); override;
175 procedure WriteBinary( const b: TBytes); overload; override;
176
Jens Geyer41f47af2019-11-09 23:24:52 +0100177 private // unit visible stuff
Jens Geyerf0e63312015-03-01 18:47:49 +0100178 class function DoubleToInt64Bits( const db : Double) : Int64;
179 class function Int64BitsToDouble( const i64 : Int64) : Double;
180
181 // Abstract method for writing the start of lists and sets. List and sets on
182 // the wire differ only by the type indicator.
183 procedure WriteCollectionBegin( const elemType : TType; size : Integer);
184
185 procedure WriteVarint64( n : UInt64);
186
187 // Convert l into a zigzag long. This allows negative numbers to be
188 // represented compactly as a varint.
189 class function longToZigzag( const n : Int64) : UInt64;
190
191 // Convert n into a zigzag int. This allows negative numbers to be
192 // represented compactly as a varint.
193 class function intToZigZag( const n : Integer) : Cardinal;
194
195 //Convert a Int64 into little-endian bytes in buf starting at off and going until off+7.
Jens Geyerf726ae32021-06-04 11:17:26 +0200196 class procedure fixedLongToBytes( const n : Int64; var buf : TEightBytesArray); inline;
Jens Geyerf0e63312015-03-01 18:47:49 +0100197
Jens Geyer41f47af2019-11-09 23:24:52 +0100198 strict protected
199 function GetMinSerializedSize( const aType : TType) : Integer; override;
200 procedure Reset; override;
201
Jens Geyerf0e63312015-03-01 18:47:49 +0100202 public
Jens Geyer17c3ad92017-09-05 20:31:27 +0200203 function ReadMessageBegin: TThriftMessage; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100204 procedure ReadMessageEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200205 function ReadStructBegin: TThriftStruct; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100206 procedure ReadStructEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200207 function ReadFieldBegin: TThriftField; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100208 procedure ReadFieldEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200209 function ReadMapBegin: TThriftMap; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100210 procedure ReadMapEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200211 function ReadListBegin: TThriftList; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100212 procedure ReadListEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200213 function ReadSetBegin: TThriftSet; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100214 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 ReadBinary: TBytes; overload; override;
222
223 private
224 // Internal Reading methods
225
226 // Read an i32 from the wire as a varint. The MSB of each byte is set
227 // if there is another byte to follow. This can Read up to 5 bytes.
228 function ReadVarint32 : Cardinal;
229
230 // Read an i64 from the wire as a proper varint. The MSB of each byte is set
231 // if there is another byte to follow. This can Read up to 10 bytes.
232 function ReadVarint64 : UInt64;
233
234
235 // encoding helpers
236
237 // Convert from zigzag Integer to Integer.
238 class function zigzagToInt( const n : Cardinal ) : Integer;
239
240 // Convert from zigzag Int64 to Int64.
241 class function zigzagToLong( const n : UInt64) : Int64;
242
243 // Note that it's important that the mask bytes are Int64 literals,
244 // otherwise they'll default to ints, and when you shift an Integer left 56 bits,
245 // you just get a messed up Integer.
Jens Geyerf726ae32021-06-04 11:17:26 +0200246 class function bytesToLong( const bytes : TEightBytesArray) : Int64; inline;
Jens Geyerf0e63312015-03-01 18:47:49 +0100247
248 // type testing and converting
249 class function isBoolType( const b : byte) : Boolean;
250
251 // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
252 class function getTType( const type_ : byte) : TType;
253
254 // Given a TType value, find the appropriate TCompactProtocol.Types constant.
255 class function getCompactType( const ttype : TType) : Byte;
256 end;
257
258
259implementation
260
261
262
263//--- TCompactProtocolImpl.TFactory ----------------------------------------
264
265
266function TCompactProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
267begin
268 result := TCompactProtocolImpl.Create( trans);
269end;
270
271
272//--- TCompactProtocolImpl -------------------------------------------------
273
274
Jens Geyera019cda2019-11-09 23:24:52 +0100275constructor TCompactProtocolImpl.Create( const trans : ITransport);
Jens Geyerf0e63312015-03-01 18:47:49 +0100276begin
277 inherited Create( trans);
278
279 lastFieldId_ := 0;
280 lastField_ := TStack<Integer>.Create;
281
Jens Geyer17c3ad92017-09-05 20:31:27 +0200282 Init( booleanField_, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100283 boolValue_ := unused;
284end;
285
286
287destructor TCompactProtocolImpl.Destroy;
288begin
289 try
290 FreeAndNil( lastField_);
291 finally
292 inherited Destroy;
293 end;
294end;
295
296
297
298procedure TCompactProtocolImpl.Reset;
299begin
Jens Geyer41f47af2019-11-09 23:24:52 +0100300 inherited Reset;
Jens Geyerf0e63312015-03-01 18:47:49 +0100301 lastField_.Clear();
302 lastFieldId_ := 0;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200303 Init( booleanField_, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100304 boolValue_ := unused;
305end;
306
307
308// Writes a byte without any possibility of all that field header nonsense.
309// Used internally by other writing methods that know they need to Write a byte.
310procedure TCompactProtocolImpl.WriteByteDirect( const b : Byte);
Jens Geyerf0e63312015-03-01 18:47:49 +0100311begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200312 Transport.Write( @b, SizeOf(b));
Jens Geyerf0e63312015-03-01 18:47:49 +0100313end;
314
315
316// Writes a byte without any possibility of all that field header nonsense.
317procedure TCompactProtocolImpl.WriteByteDirect( const n : Integer);
318begin
319 WriteByteDirect( Byte(n));
320end;
321
322
323// Write an i32 as a varint. Results in 1-5 bytes on the wire.
324procedure TCompactProtocolImpl.WriteVarint32( n : Cardinal);
Jens Geyerf726ae32021-06-04 11:17:26 +0200325var idx : Integer;
326 i32buf : packed array[0..4] of Byte;
Jens Geyerf0e63312015-03-01 18:47:49 +0100327begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100328 idx := 0;
329 while TRUE do begin
330 ASSERT( idx < Length(i32buf));
331
332 // last part?
333 if ((n and not $7F) = 0) then begin
334 i32buf[idx] := Byte(n);
335 Inc(idx);
336 Break;
337 end;
338
339 i32buf[idx] := Byte((n and $7F) or $80);
340 Inc(idx);
341 n := n shr 7;
342 end;
343
Jens Geyerf726ae32021-06-04 11:17:26 +0200344 Transport.Write( @i32buf[0], 0, idx);
Jens Geyerf0e63312015-03-01 18:47:49 +0100345end;
346
347
348// Write a message header to the wire. Compact Protocol messages contain the
349// protocol version so we can migrate forwards in the future if need be.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200350procedure TCompactProtocolImpl.WriteMessageBegin( const msg: TThriftMessage);
Jens Geyerf0e63312015-03-01 18:47:49 +0100351var versionAndType : Byte;
352begin
353 Reset;
354
355 versionAndType := Byte( VERSION and VERSION_MASK)
356 or Byte( (Cardinal(msg.Type_) shl TYPE_SHIFT_AMOUNT) and TYPE_MASK);
357
358 WriteByteDirect( PROTOCOL_ID);
359 WriteByteDirect( versionAndType);
360 WriteVarint32( Cardinal(msg.SeqID));
361 WriteString( msg.Name);
362end;
363
364
365// Write a struct begin. This doesn't actually put anything on the wire. We use it as an
366// opportunity to put special placeholder markers on the field stack so we can get the
367// field id deltas correct.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200368procedure TCompactProtocolImpl.WriteStructBegin( const struc: TThriftStruct);
Jens Geyerf0e63312015-03-01 18:47:49 +0100369begin
370 lastField_.Push(lastFieldId_);
371 lastFieldId_ := 0;
372end;
373
374
375// Write a struct end. This doesn't actually put anything on the wire. We use this as an
376// opportunity to pop the last field from the current struct off of the field stack.
377procedure TCompactProtocolImpl.WriteStructEnd;
378begin
379 lastFieldId_ := lastField_.Pop();
380end;
381
382
383// Write a field header containing the field id and field type. If the difference between the
384// current field id and the last one is small (< 15), then the field id will be encoded in
385// 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 +0200386procedure TCompactProtocolImpl.WriteFieldBegin( const field: TThriftField);
Jens Geyerf0e63312015-03-01 18:47:49 +0100387begin
388 case field.Type_ of
389 TType.Bool_ : booleanField_ := field; // we want to possibly include the value, so we'll wait.
390 else
391 WriteFieldBeginInternal(field, $FF);
392 end;
393end;
394
395
396// The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
397// of the type header. This is used specifically in the boolean field case.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200398procedure TCompactProtocolImpl.WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
Jens Geyerf0e63312015-03-01 18:47:49 +0100399var typeToWrite : Byte;
400begin
401 // if there's a type override, use that.
402 if typeOverride = $FF
403 then typeToWrite := getCompactType( field.Type_)
404 else typeToWrite := typeOverride;
405
406 // check if we can use delta encoding for the field id
407 if (field.ID > lastFieldId_) and ((field.ID - lastFieldId_) <= 15)
408 then begin
409 // Write them together
410 WriteByteDirect( ((field.ID - lastFieldId_) shl 4) or typeToWrite);
411 end
412 else begin
413 // Write them separate
414 WriteByteDirect( typeToWrite);
415 WriteI16( field.ID);
416 end;
417
418 lastFieldId_ := field.ID;
419end;
420
421
422// Write the STOP symbol so we know there are no more fields in this struct.
423procedure TCompactProtocolImpl.WriteFieldStop;
424begin
425 WriteByteDirect( Byte( Types.STOP));
426end;
427
428
429// Write a map header. If the map is empty, omit the key and value type
430// headers, as we don't need any additional information to skip it.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200431procedure TCompactProtocolImpl.WriteMapBegin( const map: TThriftMap);
Jens Geyerf0e63312015-03-01 18:47:49 +0100432var key, val : Byte;
433begin
434 if (map.Count = 0)
435 then WriteByteDirect( 0)
436 else begin
437 WriteVarint32( Cardinal( map.Count));
438 key := getCompactType(map.KeyType);
439 val := getCompactType(map.ValueType);
440 WriteByteDirect( (key shl 4) or val);
441 end;
442end;
443
444
445// Write a list header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200446procedure TCompactProtocolImpl.WriteListBegin( const list: TThriftList);
Jens Geyerf0e63312015-03-01 18:47:49 +0100447begin
448 WriteCollectionBegin( list.ElementType, list.Count);
449end;
450
451
452// Write a set header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200453procedure TCompactProtocolImpl.WriteSetBegin( const set_: TThriftSet );
Jens Geyerf0e63312015-03-01 18:47:49 +0100454begin
455 WriteCollectionBegin( set_.ElementType, set_.Count);
456end;
457
458
459// Write a boolean value. Potentially, this could be a boolean field, in
460// which case the field header info isn't written yet. If so, decide what the
461// right type header is for the value and then Write the field header.
462// Otherwise, Write a single byte.
463procedure TCompactProtocolImpl.WriteBool( b: Boolean);
464var bt : Types;
465begin
466 if b
467 then bt := Types.BOOLEAN_TRUE
468 else bt := Types.BOOLEAN_FALSE;
469
Jens Geyer17c3ad92017-09-05 20:31:27 +0200470 if booleanField_.Type_ = TType.Bool_ then begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100471 // we haven't written the field header yet
472 WriteFieldBeginInternal( booleanField_, Byte(bt));
Jens Geyer17c3ad92017-09-05 20:31:27 +0200473 booleanField_.Type_ := TType.Stop;
Jens Geyerf0e63312015-03-01 18:47:49 +0100474 end
475 else begin
476 // we're not part of a field, so just Write the value.
477 WriteByteDirect( Byte(bt));
478 end;
479end;
480
481
482// Write a byte. Nothing to see here!
483procedure TCompactProtocolImpl.WriteByte( b: ShortInt);
484begin
485 WriteByteDirect( Byte(b));
486end;
487
488
489// Write an I16 as a zigzag varint.
490procedure TCompactProtocolImpl.WriteI16( i16: SmallInt);
491begin
492 WriteVarint32( intToZigZag( i16));
493end;
494
495
496// Write an i32 as a zigzag varint.
497procedure TCompactProtocolImpl.WriteI32( i32: Integer);
498begin
499 WriteVarint32( intToZigZag( i32));
500end;
501
502
503// Write an i64 as a zigzag varint.
504procedure TCompactProtocolImpl.WriteI64( const i64: Int64);
505begin
506 WriteVarint64( longToZigzag( i64));
507end;
508
509
510class function TCompactProtocolImpl.DoubleToInt64Bits( const db : Double) : Int64;
511begin
512 ASSERT( SizeOf(db) = SizeOf(result));
513 Move( db, result, SizeOf(result));
514end;
515
516
517class function TCompactProtocolImpl.Int64BitsToDouble( const i64 : Int64) : Double;
518begin
519 ASSERT( SizeOf(i64) = SizeOf(result));
520 Move( i64, result, SizeOf(result));
521end;
522
523
524// Write a double to the wire as 8 bytes.
525procedure TCompactProtocolImpl.WriteDouble( const dub: Double);
Jens Geyerf726ae32021-06-04 11:17:26 +0200526var data : TEightBytesArray;
Jens Geyerf0e63312015-03-01 18:47:49 +0100527begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100528 fixedLongToBytes( DoubleToInt64Bits(dub), data);
Jens Geyerf726ae32021-06-04 11:17:26 +0200529 Transport.Write( @data[0], 0, SizeOf(data));
Jens Geyerf0e63312015-03-01 18:47:49 +0100530end;
531
532
533// Write a byte array, using a varint for the size.
534procedure TCompactProtocolImpl.WriteBinary( const b: TBytes);
535begin
536 WriteVarint32( Cardinal(Length(b)));
537 Transport.Write( b);
538end;
539
540procedure TCompactProtocolImpl.WriteMessageEnd;
541begin
542 // nothing to do
543end;
544
545
546procedure TCompactProtocolImpl.WriteMapEnd;
547begin
548 // nothing to do
549end;
550
551
552procedure TCompactProtocolImpl.WriteListEnd;
553begin
554 // nothing to do
555end;
556
557
558procedure TCompactProtocolImpl.WriteSetEnd;
559begin
560 // nothing to do
561end;
562
563
564procedure TCompactProtocolImpl.WriteFieldEnd;
565begin
566 // nothing to do
567end;
568
569
570// Abstract method for writing the start of lists and sets. List and sets on
571// the wire differ only by the type indicator.
572procedure TCompactProtocolImpl.WriteCollectionBegin( const elemType : TType; size : Integer);
573begin
574 if size <= 14
575 then WriteByteDirect( (size shl 4) or getCompactType(elemType))
576 else begin
577 WriteByteDirect( $F0 or getCompactType(elemType));
578 WriteVarint32( Cardinal(size));
579 end;
580end;
581
582
583// Write an i64 as a varint. Results in 1-10 bytes on the wire.
584procedure TCompactProtocolImpl.WriteVarint64( n : UInt64);
Jens Geyerf726ae32021-06-04 11:17:26 +0200585var idx : Integer;
586 varint64out : packed array[0..9] of Byte;
Jens Geyerf0e63312015-03-01 18:47:49 +0100587begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100588 idx := 0;
589 while TRUE do begin
590 ASSERT( idx < Length(varint64out));
591
592 // last one?
593 if (n and not UInt64($7F)) = 0 then begin
594 varint64out[idx] := Byte(n);
595 Inc(idx);
596 Break;
597 end;
598
599 varint64out[idx] := Byte((n and $7F) or $80);
600 Inc(idx);
601 n := n shr 7;
602 end;
603
Jens Geyerf726ae32021-06-04 11:17:26 +0200604 Transport.Write( @varint64out[0], 0, idx);
Jens Geyerf0e63312015-03-01 18:47:49 +0100605end;
606
607
608// Convert l into a zigzag Int64. This allows negative numbers to be
609// represented compactly as a varint.
610class function TCompactProtocolImpl.longToZigzag( const n : Int64) : UInt64;
611begin
612 // there is no arithmetic right shift in Delphi
613 if n >= 0
614 then result := UInt64(n shl 1)
615 else result := UInt64(n shl 1) xor $FFFFFFFFFFFFFFFF;
616end;
617
618
619// Convert n into a zigzag Integer. This allows negative numbers to be
620// represented compactly as a varint.
621class function TCompactProtocolImpl.intToZigZag( const n : Integer) : Cardinal;
622begin
623 // there is no arithmetic right shift in Delphi
624 if n >= 0
625 then result := Cardinal(n shl 1)
626 else result := Cardinal(n shl 1) xor $FFFFFFFF;
627end;
628
629
630// Convert a Int64 into 8 little-endian bytes in buf
Jens Geyerf726ae32021-06-04 11:17:26 +0200631class procedure TCompactProtocolImpl.fixedLongToBytes( const n : Int64; var buf : TEightBytesArray);
Jens Geyerf0e63312015-03-01 18:47:49 +0100632begin
Jens Geyerf726ae32021-06-04 11:17:26 +0200633 ASSERT( Length(buf) >= 8);
Jens Geyerf0e63312015-03-01 18:47:49 +0100634 buf[0] := Byte( n and $FF);
635 buf[1] := Byte((n shr 8) and $FF);
636 buf[2] := Byte((n shr 16) and $FF);
637 buf[3] := Byte((n shr 24) and $FF);
638 buf[4] := Byte((n shr 32) and $FF);
639 buf[5] := Byte((n shr 40) and $FF);
640 buf[6] := Byte((n shr 48) and $FF);
641 buf[7] := Byte((n shr 56) and $FF);
642end;
643
644
645
646// Read a message header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200647function TCompactProtocolImpl.ReadMessageBegin : TThriftMessage;
Jens Geyerf0e63312015-03-01 18:47:49 +0100648var protocolId, versionAndType, version, type_ : Byte;
649 seqid : Integer;
650 msgNm : String;
651begin
652 Reset;
653
654 protocolId := Byte( ReadByte);
655 if (protocolId <> PROTOCOL_ID)
Jens Geyere0e32402016-04-20 21:50:48 +0200656 then raise TProtocolExceptionBadVersion.Create( 'Expected protocol id ' + IntToHex(PROTOCOL_ID,2)
657 + ' but got ' + IntToHex(protocolId,2));
Jens Geyerf0e63312015-03-01 18:47:49 +0100658
659 versionAndType := Byte( ReadByte);
660 version := Byte( versionAndType and VERSION_MASK);
661 if (version <> VERSION)
Jens Geyere0e32402016-04-20 21:50:48 +0200662 then raise TProtocolExceptionBadVersion.Create( 'Expected version ' +IntToStr(VERSION)
663 + ' but got ' + IntToStr(version));
Jens Geyerf0e63312015-03-01 18:47:49 +0100664
665 type_ := Byte( (versionAndType shr TYPE_SHIFT_AMOUNT) and TYPE_BITS);
666 seqid := Integer( ReadVarint32);
667 msgNm := ReadString;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200668 Init( result, msgNm, TMessageType(type_), seqid);
Jens Geyerf0e63312015-03-01 18:47:49 +0100669end;
670
671
672// Read a struct begin. There's nothing on the wire for this, but it is our
673// opportunity to push a new struct begin marker onto the field stack.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200674function TCompactProtocolImpl.ReadStructBegin: TThriftStruct;
Jens Geyerf0e63312015-03-01 18:47:49 +0100675begin
676 lastField_.Push( lastFieldId_);
677 lastFieldId_ := 0;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200678 Init( result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100679end;
680
681
682// Doesn't actually consume any wire data, just removes the last field for
683// this struct from the field stack.
684procedure TCompactProtocolImpl.ReadStructEnd;
685begin
686 // consume the last field we Read off the wire.
687 lastFieldId_ := lastField_.Pop();
688end;
689
690
691// Read a field header off the wire.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200692function TCompactProtocolImpl.ReadFieldBegin: TThriftField;
Jens Geyerf0e63312015-03-01 18:47:49 +0100693var type_ : Byte;
Jens Geyera715f702019-08-28 22:56:13 +0200694 modifier : ShortInt;
695 fieldId : SmallInt;
Jens Geyerf0e63312015-03-01 18:47:49 +0100696begin
697 type_ := Byte( ReadByte);
698
699 // if it's a stop, then we can return immediately, as the struct is over.
700 if type_ = Byte(Types.STOP) then begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200701 Init( result, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100702 Exit;
703 end;
704
705 // mask off the 4 MSB of the type header. it could contain a field id delta.
706 modifier := ShortInt( (type_ and $F0) shr 4);
707 if (modifier = 0)
708 then fieldId := ReadI16 // not a delta. look ahead for the zigzag varint field id.
Jens Geyera715f702019-08-28 22:56:13 +0200709 else fieldId := SmallInt( lastFieldId_ + modifier); // add the delta to the last Read field id.
Jens Geyerf0e63312015-03-01 18:47:49 +0100710
Jens Geyer17c3ad92017-09-05 20:31:27 +0200711 Init( result, '', getTType(Byte(type_ and $0F)), fieldId);
Jens Geyerf0e63312015-03-01 18:47:49 +0100712
713 // if this happens to be a boolean field, the value is encoded in the type
714 // save the boolean value in a special instance variable.
715 if isBoolType(type_) then begin
716 if Byte(type_ and $0F) = Byte(Types.BOOLEAN_TRUE)
717 then boolValue_ := bool_true
718 else boolValue_ := bool_false;
719 end;
720
721 // push the new field onto the field stack so we can keep the deltas going.
722 lastFieldId_ := result.ID;
723end;
724
725
726// Read a map header off the wire. If the size is zero, skip Reading the key
727// and value type. This means that 0-length maps will yield TMaps without the
728// "correct" types.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200729function TCompactProtocolImpl.ReadMapBegin: TThriftMap;
Jens Geyerf0e63312015-03-01 18:47:49 +0100730var size : Integer;
731 keyAndValueType : Byte;
732 key, val : TType;
733begin
734 size := Integer( ReadVarint32);
735 if size = 0
736 then keyAndValueType := 0
737 else keyAndValueType := Byte( ReadByte);
738
739 key := getTType( Byte( keyAndValueType shr 4));
740 val := getTType( Byte( keyAndValueType and $F));
Jens Geyer17c3ad92017-09-05 20:31:27 +0200741 Init( result, key, val, size);
Jens Geyerf0e63312015-03-01 18:47:49 +0100742 ASSERT( (result.KeyType = key) and (result.ValueType = val));
Jens Geyer41f47af2019-11-09 23:24:52 +0100743 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100744end;
745
746
747// Read a list header off the wire. If the list size is 0-14, the size will
748// be packed into the element type header. If it's a longer list, the 4 MSB
749// of the element type header will be $F, and a varint will follow with the
750// true size.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200751function TCompactProtocolImpl.ReadListBegin: TThriftList;
Jens Geyerf0e63312015-03-01 18:47:49 +0100752var size_and_type : Byte;
753 size : Integer;
754 type_ : TType;
755begin
756 size_and_type := Byte( ReadByte);
757
758 size := (size_and_type shr 4) and $0F;
759 if (size = 15)
760 then size := Integer( ReadVarint32);
761
762 type_ := getTType( size_and_type);
Jens Geyer17c3ad92017-09-05 20:31:27 +0200763 Init( result, type_, size);
Jens Geyer41f47af2019-11-09 23:24:52 +0100764 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100765end;
766
767
768// Read a set header off the wire. If the set size is 0-14, the size will
769// be packed into the element type header. If it's a longer set, the 4 MSB
770// of the element type header will be $F, and a varint will follow with the
771// true size.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200772function TCompactProtocolImpl.ReadSetBegin: TThriftSet;
Jens Geyerf0e63312015-03-01 18:47:49 +0100773var size_and_type : Byte;
774 size : Integer;
775 type_ : TType;
776begin
777 size_and_type := Byte( ReadByte);
778
779 size := (size_and_type shr 4) and $0F;
780 if (size = 15)
781 then size := Integer( ReadVarint32);
782
783 type_ := getTType( size_and_type);
Jens Geyer17c3ad92017-09-05 20:31:27 +0200784 Init( result, type_, size);
Jens Geyer41f47af2019-11-09 23:24:52 +0100785 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100786end;
787
788
789// Read a boolean off the wire. If this is a boolean field, the value should
790// already have been Read during ReadFieldBegin, so we'll just consume the
791// pre-stored value. Otherwise, Read a byte.
792function TCompactProtocolImpl.ReadBool: Boolean;
793begin
794 if boolValue_ <> unused then begin
795 result := (boolValue_ = bool_true);
796 boolValue_ := unused;
797 Exit;
798 end;
799
800 result := (Byte(ReadByte) = Byte(Types.BOOLEAN_TRUE));
801end;
802
803
804// Read a single byte off the wire. Nothing interesting here.
805function TCompactProtocolImpl.ReadByte: ShortInt;
Jens Geyerf0e63312015-03-01 18:47:49 +0100806begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200807 Transport.ReadAll( @result, SizeOf(result), 0, 1);
Jens Geyerf0e63312015-03-01 18:47:49 +0100808end;
809
810
811// Read an i16 from the wire as a zigzag varint.
812function TCompactProtocolImpl.ReadI16: SmallInt;
813begin
814 result := SmallInt( zigzagToInt( ReadVarint32));
815end;
816
817
818// Read an i32 from the wire as a zigzag varint.
819function TCompactProtocolImpl.ReadI32: Integer;
820begin
821 result := zigzagToInt( ReadVarint32);
822end;
823
824
825// Read an i64 from the wire as a zigzag varint.
826function TCompactProtocolImpl.ReadI64: Int64;
827begin
828 result := zigzagToLong( ReadVarint64);
829end;
830
831
832// No magic here - just Read a double off the wire.
Jens Geyerf726ae32021-06-04 11:17:26 +0200833function TCompactProtocolImpl.ReadDouble : Double;
834var longBits : TEightBytesArray;
Jens Geyerf0e63312015-03-01 18:47:49 +0100835begin
Jens Geyerf726ae32021-06-04 11:17:26 +0200836 ASSERT( SizeOf(longBits) = SizeOf(result));
837 Transport.ReadAll( @longBits[0], SizeOf(longBits), 0, SizeOf(longBits));
Jens Geyerf0e63312015-03-01 18:47:49 +0100838 result := Int64BitsToDouble( bytesToLong( longBits));
839end;
840
841
842// Read a byte[] from the wire.
843function TCompactProtocolImpl.ReadBinary: TBytes;
844var length : Integer;
845begin
846 length := Integer( ReadVarint32);
Jens Geyer41f47af2019-11-09 23:24:52 +0100847 FTrans.CheckReadBytesAvailable(length);
Jens Geyerf0e63312015-03-01 18:47:49 +0100848 SetLength( result, length);
849 if (length > 0)
850 then Transport.ReadAll( result, 0, length);
851end;
852
853
854procedure TCompactProtocolImpl.ReadMessageEnd;
855begin
856 // nothing to do
857end;
858
859
860procedure TCompactProtocolImpl.ReadFieldEnd;
861begin
862 // nothing to do
863end;
864
865
866procedure TCompactProtocolImpl.ReadMapEnd;
867begin
868 // nothing to do
869end;
870
871
872procedure TCompactProtocolImpl.ReadListEnd;
873begin
874 // nothing to do
875end;
876
877
878procedure TCompactProtocolImpl.ReadSetEnd;
879begin
880 // nothing to do
881end;
882
883
884
885// Read an i32 from the wire as a varint. The MSB of each byte is set
886// if there is another byte to follow. This can Read up to 5 bytes.
887function TCompactProtocolImpl.ReadVarint32 : Cardinal;
888var shift : Integer;
889 b : Byte;
890begin
891 result := 0;
892 shift := 0;
893 while TRUE do begin
894 b := Byte( ReadByte);
895 result := result or (Cardinal(b and $7F) shl shift);
896 if ((b and $80) <> $80)
897 then Break;
898 Inc( shift, 7);
899 end;
900end;
901
902
903// Read an i64 from the wire as a proper varint. The MSB of each byte is set
904// if there is another byte to follow. This can Read up to 10 bytes.
905function TCompactProtocolImpl.ReadVarint64 : UInt64;
906var shift : Integer;
907 b : Byte;
908begin
909 result := 0;
910 shift := 0;
911 while TRUE do begin
912 b := Byte( ReadByte);
913 result := result or (UInt64(b and $7F) shl shift);
914 if ((b and $80) <> $80)
915 then Break;
916 Inc( shift, 7);
917 end;
918end;
919
920
921// Convert from zigzag Integer to Integer.
922class function TCompactProtocolImpl.zigzagToInt( const n : Cardinal ) : Integer;
923begin
924 result := Integer(n shr 1) xor (-Integer(n and 1));
925end;
926
927
928// Convert from zigzag Int64 to Int64.
929class function TCompactProtocolImpl.zigzagToLong( const n : UInt64) : Int64;
930begin
931 result := Int64(n shr 1) xor (-Int64(n and 1));
932end;
933
934
935// Note that it's important that the mask bytes are Int64 literals,
936// otherwise they'll default to ints, and when you shift an Integer left 56 bits,
937// you just get a messed up Integer.
Jens Geyerf726ae32021-06-04 11:17:26 +0200938class function TCompactProtocolImpl.bytesToLong( const bytes : TEightBytesArray) : Int64;
Jens Geyerf0e63312015-03-01 18:47:49 +0100939begin
940 ASSERT( Length(bytes) >= 8);
941 result := (Int64(bytes[7] and $FF) shl 56) or
942 (Int64(bytes[6] and $FF) shl 48) or
943 (Int64(bytes[5] and $FF) shl 40) or
944 (Int64(bytes[4] and $FF) shl 32) or
945 (Int64(bytes[3] and $FF) shl 24) or
946 (Int64(bytes[2] and $FF) shl 16) or
947 (Int64(bytes[1] and $FF) shl 8) or
948 (Int64(bytes[0] and $FF));
949end;
950
951
952class function TCompactProtocolImpl.isBoolType( const b : byte) : Boolean;
953var lowerNibble : Byte;
954begin
955 lowerNibble := b and $0f;
956 result := (Types(lowerNibble) in [Types.BOOLEAN_TRUE, Types.BOOLEAN_FALSE]);
957end;
958
959
960// Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
961class function TCompactProtocolImpl.getTType( const type_ : byte) : TType;
962var tct : Types;
963begin
964 tct := Types( type_ and $0F);
965 if tct in [Low(Types)..High(Types)]
966 then result := tcompactTypeToType[tct]
Jens Geyere0e32402016-04-20 21:50:48 +0200967 else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(tct)));
Jens Geyerf0e63312015-03-01 18:47:49 +0100968end;
969
970
971// Given a TType value, find the appropriate TCompactProtocol.Types constant.
972class function TCompactProtocolImpl.getCompactType( const ttype : TType) : Byte;
973begin
974 if ttype in VALID_TTYPES
975 then result := Byte( ttypeToCompactType[ttype])
Jens Geyere0e32402016-04-20 21:50:48 +0200976 else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(ttype)));
Jens Geyerf0e63312015-03-01 18:47:49 +0100977end;
978
979
Jens Geyer41f47af2019-11-09 23:24:52 +0100980function TCompactProtocolImpl.GetMinSerializedSize( const aType : TType) : Integer;
981// Return the minimum number of bytes a type will consume on the wire
982begin
983 case aType of
984 TType.Stop: result := 0;
985 TType.Void: result := 0;
986 TType.Bool_: result := SizeOf(Byte);
987 TType.Byte_: result := SizeOf(Byte);
988 TType.Double_: result := 8; // uses fixedLongToBytes() which always writes 8 bytes
989 TType.I16: result := SizeOf(Byte);
990 TType.I32: result := SizeOf(Byte);
991 TType.I64: result := SizeOf(Byte);
992 TType.String_: result := SizeOf(Byte); // string length
993 TType.Struct: result := 0; // empty struct
994 TType.Map: result := SizeOf(Byte); // element count
995 TType.Set_: result := SizeOf(Byte); // element count
996 TType.List: result := SizeOf(Byte); // element count
997 else
998 raise TTransportExceptionBadArgs.Create('Unhandled type code');
999 end;
1000end;
1001
1002
1003
1004
1005
Jens Geyerf0e63312015-03-01 18:47:49 +01001006//--- unit tests -------------------------------------------
1007
1008{$IFDEF Debug}
1009procedure TestDoubleToInt64Bits;
1010
1011 procedure TestPair( const a : Double; const b : Int64);
1012 begin
1013 ASSERT( TCompactProtocolImpl.DoubleToInt64Bits(a) = b);
1014 ASSERT( TCompactProtocolImpl.Int64BitsToDouble(b) = a);
1015 end;
1016
1017begin
1018 TestPair( 1.0000000000000000E+000, Int64($3FF0000000000000));
1019 TestPair( 1.5000000000000000E+001, Int64($402E000000000000));
1020 TestPair( 2.5500000000000000E+002, Int64($406FE00000000000));
1021 TestPair( 4.2949672950000000E+009, Int64($41EFFFFFFFE00000));
1022 TestPair( 3.9062500000000000E-003, Int64($3F70000000000000));
1023 TestPair( 2.3283064365386963E-010, Int64($3DF0000000000000));
1024 TestPair( 1.2345678901230000E-300, Int64($01AA74FE1C1E7E45));
1025 TestPair( 1.2345678901234500E-150, Int64($20D02A36586DB4BB));
1026 TestPair( 1.2345678901234565E+000, Int64($3FF3C0CA428C59FA));
1027 TestPair( 1.2345678901234567E+000, Int64($3FF3C0CA428C59FB));
1028 TestPair( 1.2345678901234569E+000, Int64($3FF3C0CA428C59FC));
1029 TestPair( 1.2345678901234569E+150, Int64($5F182344CD3CDF9F));
1030 TestPair( 1.2345678901234569E+300, Int64($7E3D7EE8BCBBD352));
1031 TestPair( -1.7976931348623157E+308, Int64($FFEFFFFFFFFFFFFF));
1032 TestPair( 1.7976931348623157E+308, Int64($7FEFFFFFFFFFFFFF));
1033 TestPair( 4.9406564584124654E-324, Int64($0000000000000001));
1034 TestPair( 0.0000000000000000E+000, Int64($0000000000000000));
1035 TestPair( 4.94065645841247E-324, Int64($0000000000000001));
1036 TestPair( 3.2378592100206092E-319, Int64($000000000000FFFF));
1037 TestPair( 1.3906711615669959E-309, Int64($0000FFFFFFFFFFFF));
1038 TestPair( NegInfinity, Int64($FFF0000000000000));
1039 TestPair( Infinity, Int64($7FF0000000000000));
1040
1041 // NaN is special
1042 ASSERT( TCompactProtocolImpl.DoubleToInt64Bits( NaN) = Int64($FFF8000000000000));
1043 ASSERT( IsNan( TCompactProtocolImpl.Int64BitsToDouble( Int64($FFF8000000000000))));
1044end;
1045{$ENDIF}
1046
1047
1048{$IFDEF Debug}
1049procedure TestZigZag;
1050
1051 procedure Test32( const test : Integer);
1052 var zz : Cardinal;
1053 begin
1054 zz := TCompactProtocolImpl.intToZigZag(test);
1055 ASSERT( TCompactProtocolImpl.zigzagToInt(zz) = test, IntToStr(test));
1056 end;
1057
1058 procedure Test64( const test : Int64);
1059 var zz : UInt64;
1060 begin
1061 zz := TCompactProtocolImpl.longToZigzag(test);
1062 ASSERT( TCompactProtocolImpl.zigzagToLong(zz) = test, IntToStr(test));
1063 end;
1064
1065var i : Integer;
1066begin
1067 // protobuf testcases
Jens Geyerd6834402015-03-07 13:16:34 +01001068 ASSERT( TCompactProtocolImpl.intToZigZag(0) = 0, 'pb #1 to ZigZag');
1069 ASSERT( TCompactProtocolImpl.intToZigZag(-1) = 1, 'pb #2 to ZigZag');
1070 ASSERT( TCompactProtocolImpl.intToZigZag(1) = 2, 'pb #3 to ZigZag');
1071 ASSERT( TCompactProtocolImpl.intToZigZag(-2) = 3, 'pb #4 to ZigZag');
1072 ASSERT( TCompactProtocolImpl.intToZigZag(+2147483647) = 4294967294, 'pb #5 to ZigZag');
1073 ASSERT( TCompactProtocolImpl.intToZigZag(-2147483648) = 4294967295, 'pb #6 to ZigZag');
1074
1075 // protobuf testcases
1076 ASSERT( TCompactProtocolImpl.zigzagToInt(0) = 0, 'pb #1 from ZigZag');
1077 ASSERT( TCompactProtocolImpl.zigzagToInt(1) = -1, 'pb #2 from ZigZag');
1078 ASSERT( TCompactProtocolImpl.zigzagToInt(2) = 1, 'pb #3 from ZigZag');
1079 ASSERT( TCompactProtocolImpl.zigzagToInt(3) = -2, 'pb #4 from ZigZag');
1080 ASSERT( TCompactProtocolImpl.zigzagToInt(4294967294) = +2147483647, 'pb #5 from ZigZag');
1081 ASSERT( TCompactProtocolImpl.zigzagToInt(4294967295) = -2147483648, 'pb #6 from ZigZag');
Jens Geyerf0e63312015-03-01 18:47:49 +01001082
1083 // back and forth 32
1084 Test32( 0);
1085 for i := 0 to 30 do begin
1086 Test32( +(Integer(1) shl i));
1087 Test32( -(Integer(1) shl i));
1088 end;
1089 Test32( Integer($7FFFFFFF));
1090 Test32( Integer($80000000));
1091
1092 // back and forth 64
1093 Test64( 0);
1094 for i := 0 to 62 do begin
1095 Test64( +(Int64(1) shl i));
1096 Test64( -(Int64(1) shl i));
1097 end;
1098 Test64( Int64($7FFFFFFFFFFFFFFF));
1099 Test64( Int64($8000000000000000));
1100end;
1101{$ENDIF}
1102
1103
Jens Geyera6ea4442015-03-02 23:06:57 +01001104{$IFDEF Debug}
1105procedure TestLongBytes;
1106
1107 procedure Test( const test : Int64);
Jens Geyerf726ae32021-06-04 11:17:26 +02001108 var buf : TCompactProtocolImpl.TEightBytesArray;
Jens Geyera6ea4442015-03-02 23:06:57 +01001109 begin
1110 TCompactProtocolImpl.fixedLongToBytes( test, buf);
1111 ASSERT( TCompactProtocolImpl.bytesToLong( buf) = test, IntToStr(test));
1112 end;
1113
1114var i : Integer;
1115begin
1116 Test( 0);
1117 for i := 0 to 62 do begin
1118 Test( +(Int64(1) shl i));
1119 Test( -(Int64(1) shl i));
1120 end;
1121 Test( Int64($7FFFFFFFFFFFFFFF));
1122 Test( Int64($8000000000000000));
1123end;
1124{$ENDIF}
1125
1126
Jens Geyera9235802018-09-25 00:21:12 +02001127{$IFDEF Debug}
1128procedure UnitTest;
1129var w : WORD;
1130const FPU_CW_DENORMALIZED = $0002;
1131begin
1132 w := Get8087CW;
1133 try
1134 Set8087CW( w or FPU_CW_DENORMALIZED);
1135
1136 TestDoubleToInt64Bits;
1137 TestZigZag;
1138 TestLongBytes;
1139
1140 finally
1141 Set8087CW( w);
1142 end;
1143end;
1144{$ENDIF}
1145
1146
Jens Geyerf0e63312015-03-01 18:47:49 +01001147initialization
1148 {$IFDEF Debug}
Jens Geyera9235802018-09-25 00:21:12 +02001149 UnitTest;
Jens Geyerf0e63312015-03-01 18:47:49 +01001150 {$ENDIF}
1151
1152end.
1153