blob: a8ad53a4192251b404dcf14dbc79eaccbe3a74c3 [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 Geyerb53fa8e2024-03-08 00:33:22 +0100179 procedure WriteBinary( const bytes : IThriftBytes); overload; override;
Jens Geyer62445c12022-06-29 00:00:00 +0200180 procedure WriteUuid( const uuid: TGuid); override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100181
Jens Geyer41f47af2019-11-09 23:24:52 +0100182 private // unit visible stuff
Jens Geyerf0e63312015-03-01 18:47:49 +0100183 class function DoubleToInt64Bits( const db : Double) : Int64;
184 class function Int64BitsToDouble( const i64 : Int64) : Double;
185
186 // Abstract method for writing the start of lists and sets. List and sets on
187 // the wire differ only by the type indicator.
188 procedure WriteCollectionBegin( const elemType : TType; size : Integer);
189
190 procedure WriteVarint64( n : UInt64);
191
192 // Convert l into a zigzag long. This allows negative numbers to be
193 // represented compactly as a varint.
194 class function longToZigzag( const n : Int64) : UInt64;
195
196 // Convert n into a zigzag int. This allows negative numbers to be
197 // represented compactly as a varint.
198 class function intToZigZag( const n : Integer) : Cardinal;
199
200 //Convert a Int64 into little-endian bytes in buf starting at off and going until off+7.
Jens Geyerf726ae32021-06-04 11:17:26 +0200201 class procedure fixedLongToBytes( const n : Int64; var buf : TEightBytesArray); inline;
Jens Geyerf0e63312015-03-01 18:47:49 +0100202
Jens Geyer41f47af2019-11-09 23:24:52 +0100203 strict protected
204 function GetMinSerializedSize( const aType : TType) : Integer; override;
205 procedure Reset; override;
206
Jens Geyerf0e63312015-03-01 18:47:49 +0100207 public
Jens Geyer17c3ad92017-09-05 20:31:27 +0200208 function ReadMessageBegin: TThriftMessage; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100209 procedure ReadMessageEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200210 function ReadStructBegin: TThriftStruct; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100211 procedure ReadStructEnd; override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200212 function ReadFieldBegin: TThriftField; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100213 procedure ReadFieldEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200214 function ReadMapBegin: TThriftMap; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100215 procedure ReadMapEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200216 function ReadListBegin: TThriftList; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100217 procedure ReadListEnd(); override;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200218 function ReadSetBegin: TThriftSet; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100219 procedure ReadSetEnd(); override;
220 function ReadBool: Boolean; override;
221 function ReadByte: ShortInt; override;
222 function ReadI16: SmallInt; override;
223 function ReadI32: Integer; override;
224 function ReadI64: Int64; override;
225 function ReadDouble:Double; override;
226 function ReadBinary: TBytes; overload; override;
Jens Geyer62445c12022-06-29 00:00:00 +0200227 function ReadUuid: TGuid; override;
Jens Geyerf0e63312015-03-01 18:47:49 +0100228
229 private
230 // Internal Reading methods
231
232 // Read an i32 from the wire as a varint. The MSB of each byte is set
233 // if there is another byte to follow. This can Read up to 5 bytes.
234 function ReadVarint32 : Cardinal;
235
236 // Read an i64 from the wire as a proper varint. The MSB of each byte is set
237 // if there is another byte to follow. This can Read up to 10 bytes.
238 function ReadVarint64 : UInt64;
239
240
241 // encoding helpers
242
243 // Convert from zigzag Integer to Integer.
244 class function zigzagToInt( const n : Cardinal ) : Integer;
245
246 // Convert from zigzag Int64 to Int64.
247 class function zigzagToLong( const n : UInt64) : Int64;
248
249 // Note that it's important that the mask bytes are Int64 literals,
250 // otherwise they'll default to ints, and when you shift an Integer left 56 bits,
251 // you just get a messed up Integer.
Jens Geyerf726ae32021-06-04 11:17:26 +0200252 class function bytesToLong( const bytes : TEightBytesArray) : Int64; inline;
Jens Geyerf0e63312015-03-01 18:47:49 +0100253
254 // type testing and converting
255 class function isBoolType( const b : byte) : Boolean;
256
257 // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
258 class function getTType( const type_ : byte) : TType;
259
260 // Given a TType value, find the appropriate TCompactProtocol.Types constant.
261 class function getCompactType( const ttype : TType) : Byte;
262 end;
263
264
265implementation
266
267
268
269//--- TCompactProtocolImpl.TFactory ----------------------------------------
270
271
272function TCompactProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
273begin
274 result := TCompactProtocolImpl.Create( trans);
275end;
276
277
278//--- TCompactProtocolImpl -------------------------------------------------
279
280
Jens Geyera019cda2019-11-09 23:24:52 +0100281constructor TCompactProtocolImpl.Create( const trans : ITransport);
Jens Geyerf0e63312015-03-01 18:47:49 +0100282begin
283 inherited Create( trans);
284
285 lastFieldId_ := 0;
286 lastField_ := TStack<Integer>.Create;
287
Jens Geyer17c3ad92017-09-05 20:31:27 +0200288 Init( booleanField_, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100289 boolValue_ := unused;
290end;
291
292
293destructor TCompactProtocolImpl.Destroy;
294begin
295 try
296 FreeAndNil( lastField_);
297 finally
298 inherited Destroy;
299 end;
300end;
301
302
303
304procedure TCompactProtocolImpl.Reset;
305begin
Jens Geyer41f47af2019-11-09 23:24:52 +0100306 inherited Reset;
Jens Geyerf0e63312015-03-01 18:47:49 +0100307 lastField_.Clear();
308 lastFieldId_ := 0;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200309 Init( booleanField_, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100310 boolValue_ := unused;
311end;
312
313
314// Writes a byte without any possibility of all that field header nonsense.
315// Used internally by other writing methods that know they need to Write a byte.
316procedure TCompactProtocolImpl.WriteByteDirect( const b : Byte);
Jens Geyerf0e63312015-03-01 18:47:49 +0100317begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200318 Transport.Write( @b, SizeOf(b));
Jens Geyerf0e63312015-03-01 18:47:49 +0100319end;
320
321
322// Writes a byte without any possibility of all that field header nonsense.
323procedure TCompactProtocolImpl.WriteByteDirect( const n : Integer);
324begin
325 WriteByteDirect( Byte(n));
326end;
327
328
329// Write an i32 as a varint. Results in 1-5 bytes on the wire.
330procedure TCompactProtocolImpl.WriteVarint32( n : Cardinal);
Jens Geyerf726ae32021-06-04 11:17:26 +0200331var idx : Integer;
332 i32buf : packed array[0..4] of Byte;
Jens Geyerf0e63312015-03-01 18:47:49 +0100333begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100334 idx := 0;
335 while TRUE do begin
336 ASSERT( idx < Length(i32buf));
337
338 // last part?
339 if ((n and not $7F) = 0) then begin
340 i32buf[idx] := Byte(n);
341 Inc(idx);
342 Break;
343 end;
344
345 i32buf[idx] := Byte((n and $7F) or $80);
346 Inc(idx);
347 n := n shr 7;
348 end;
349
Jens Geyerf726ae32021-06-04 11:17:26 +0200350 Transport.Write( @i32buf[0], 0, idx);
Jens Geyerf0e63312015-03-01 18:47:49 +0100351end;
352
353
354// Write a message header to the wire. Compact Protocol messages contain the
355// protocol version so we can migrate forwards in the future if need be.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200356procedure TCompactProtocolImpl.WriteMessageBegin( const msg: TThriftMessage);
Jens Geyerf0e63312015-03-01 18:47:49 +0100357var versionAndType : Byte;
358begin
359 Reset;
360
361 versionAndType := Byte( VERSION and VERSION_MASK)
362 or Byte( (Cardinal(msg.Type_) shl TYPE_SHIFT_AMOUNT) and TYPE_MASK);
363
364 WriteByteDirect( PROTOCOL_ID);
365 WriteByteDirect( versionAndType);
366 WriteVarint32( Cardinal(msg.SeqID));
367 WriteString( msg.Name);
368end;
369
370
371// Write a struct begin. This doesn't actually put anything on the wire. We use it as an
372// opportunity to put special placeholder markers on the field stack so we can get the
373// field id deltas correct.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200374procedure TCompactProtocolImpl.WriteStructBegin( const struc: TThriftStruct);
Jens Geyerf0e63312015-03-01 18:47:49 +0100375begin
376 lastField_.Push(lastFieldId_);
377 lastFieldId_ := 0;
378end;
379
380
381// Write a struct end. This doesn't actually put anything on the wire. We use this as an
382// opportunity to pop the last field from the current struct off of the field stack.
383procedure TCompactProtocolImpl.WriteStructEnd;
384begin
385 lastFieldId_ := lastField_.Pop();
386end;
387
388
389// Write a field header containing the field id and field type. If the difference between the
390// current field id and the last one is small (< 15), then the field id will be encoded in
391// 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 +0200392procedure TCompactProtocolImpl.WriteFieldBegin( const field: TThriftField);
Jens Geyerf0e63312015-03-01 18:47:49 +0100393begin
394 case field.Type_ of
395 TType.Bool_ : booleanField_ := field; // we want to possibly include the value, so we'll wait.
396 else
397 WriteFieldBeginInternal(field, $FF);
398 end;
399end;
400
401
402// The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
403// of the type header. This is used specifically in the boolean field case.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200404procedure TCompactProtocolImpl.WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
Jens Geyerf0e63312015-03-01 18:47:49 +0100405var typeToWrite : Byte;
406begin
407 // if there's a type override, use that.
408 if typeOverride = $FF
409 then typeToWrite := getCompactType( field.Type_)
410 else typeToWrite := typeOverride;
411
412 // check if we can use delta encoding for the field id
413 if (field.ID > lastFieldId_) and ((field.ID - lastFieldId_) <= 15)
414 then begin
415 // Write them together
416 WriteByteDirect( ((field.ID - lastFieldId_) shl 4) or typeToWrite);
417 end
418 else begin
419 // Write them separate
420 WriteByteDirect( typeToWrite);
421 WriteI16( field.ID);
422 end;
423
424 lastFieldId_ := field.ID;
425end;
426
427
428// Write the STOP symbol so we know there are no more fields in this struct.
429procedure TCompactProtocolImpl.WriteFieldStop;
430begin
431 WriteByteDirect( Byte( Types.STOP));
432end;
433
434
435// Write a map header. If the map is empty, omit the key and value type
436// headers, as we don't need any additional information to skip it.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200437procedure TCompactProtocolImpl.WriteMapBegin( const map: TThriftMap);
Jens Geyerf0e63312015-03-01 18:47:49 +0100438var key, val : Byte;
439begin
440 if (map.Count = 0)
441 then WriteByteDirect( 0)
442 else begin
443 WriteVarint32( Cardinal( map.Count));
444 key := getCompactType(map.KeyType);
445 val := getCompactType(map.ValueType);
446 WriteByteDirect( (key shl 4) or val);
447 end;
448end;
449
450
451// Write a list header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200452procedure TCompactProtocolImpl.WriteListBegin( const list: TThriftList);
Jens Geyerf0e63312015-03-01 18:47:49 +0100453begin
454 WriteCollectionBegin( list.ElementType, list.Count);
455end;
456
457
458// Write a set header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200459procedure TCompactProtocolImpl.WriteSetBegin( const set_: TThriftSet );
Jens Geyerf0e63312015-03-01 18:47:49 +0100460begin
461 WriteCollectionBegin( set_.ElementType, set_.Count);
462end;
463
464
465// Write a boolean value. Potentially, this could be a boolean field, in
466// which case the field header info isn't written yet. If so, decide what the
467// right type header is for the value and then Write the field header.
468// Otherwise, Write a single byte.
469procedure TCompactProtocolImpl.WriteBool( b: Boolean);
470var bt : Types;
471begin
472 if b
473 then bt := Types.BOOLEAN_TRUE
474 else bt := Types.BOOLEAN_FALSE;
475
Jens Geyer17c3ad92017-09-05 20:31:27 +0200476 if booleanField_.Type_ = TType.Bool_ then begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100477 // we haven't written the field header yet
478 WriteFieldBeginInternal( booleanField_, Byte(bt));
Jens Geyer17c3ad92017-09-05 20:31:27 +0200479 booleanField_.Type_ := TType.Stop;
Jens Geyerf0e63312015-03-01 18:47:49 +0100480 end
481 else begin
482 // we're not part of a field, so just Write the value.
483 WriteByteDirect( Byte(bt));
484 end;
485end;
486
487
488// Write a byte. Nothing to see here!
489procedure TCompactProtocolImpl.WriteByte( b: ShortInt);
490begin
491 WriteByteDirect( Byte(b));
492end;
493
494
495// Write an I16 as a zigzag varint.
496procedure TCompactProtocolImpl.WriteI16( i16: SmallInt);
497begin
498 WriteVarint32( intToZigZag( i16));
499end;
500
501
502// Write an i32 as a zigzag varint.
503procedure TCompactProtocolImpl.WriteI32( i32: Integer);
504begin
505 WriteVarint32( intToZigZag( i32));
506end;
507
508
509// Write an i64 as a zigzag varint.
510procedure TCompactProtocolImpl.WriteI64( const i64: Int64);
511begin
512 WriteVarint64( longToZigzag( i64));
513end;
514
515
516class function TCompactProtocolImpl.DoubleToInt64Bits( const db : Double) : Int64;
517begin
518 ASSERT( SizeOf(db) = SizeOf(result));
519 Move( db, result, SizeOf(result));
520end;
521
522
523class function TCompactProtocolImpl.Int64BitsToDouble( const i64 : Int64) : Double;
524begin
525 ASSERT( SizeOf(i64) = SizeOf(result));
526 Move( i64, result, SizeOf(result));
527end;
528
529
530// Write a double to the wire as 8 bytes.
531procedure TCompactProtocolImpl.WriteDouble( const dub: Double);
Jens Geyerf726ae32021-06-04 11:17:26 +0200532var data : TEightBytesArray;
Jens Geyerf0e63312015-03-01 18:47:49 +0100533begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100534 fixedLongToBytes( DoubleToInt64Bits(dub), data);
Jens Geyerf726ae32021-06-04 11:17:26 +0200535 Transport.Write( @data[0], 0, SizeOf(data));
Jens Geyerf0e63312015-03-01 18:47:49 +0100536end;
537
538
539// Write a byte array, using a varint for the size.
540procedure TCompactProtocolImpl.WriteBinary( const b: TBytes);
541begin
542 WriteVarint32( Cardinal(Length(b)));
543 Transport.Write( b);
544end;
545
Jens Geyerb53fa8e2024-03-08 00:33:22 +0100546
547procedure TCompactProtocolImpl.WriteBinary( const bytes : IThriftBytes);
548begin
549 WriteVarint32( Cardinal(bytes.Count));
550 Transport.Write( bytes.QueryRawDataPtr, 0, bytes.Count);
551end;
552
553
Jens Geyer62445c12022-06-29 00:00:00 +0200554procedure TCompactProtocolImpl.WriteUuid( const uuid: TGuid);
555var network : TGuid; // in network order (Big Endian)
556begin
557 ASSERT( SizeOf(uuid) = 16);
Jens Geyerf8f62782022-09-10 00:55:02 +0200558 network := GuidUtils.SwapByteOrder(uuid);
Jens Geyer62445c12022-06-29 00:00:00 +0200559 Transport.Write( @network, 0, SizeOf(network));
560end;
561
Jens Geyerf0e63312015-03-01 18:47:49 +0100562procedure TCompactProtocolImpl.WriteMessageEnd;
563begin
564 // nothing to do
565end;
566
567
568procedure TCompactProtocolImpl.WriteMapEnd;
569begin
570 // nothing to do
571end;
572
573
574procedure TCompactProtocolImpl.WriteListEnd;
575begin
576 // nothing to do
577end;
578
579
580procedure TCompactProtocolImpl.WriteSetEnd;
581begin
582 // nothing to do
583end;
584
585
586procedure TCompactProtocolImpl.WriteFieldEnd;
587begin
588 // nothing to do
589end;
590
591
592// Abstract method for writing the start of lists and sets. List and sets on
593// the wire differ only by the type indicator.
594procedure TCompactProtocolImpl.WriteCollectionBegin( const elemType : TType; size : Integer);
595begin
596 if size <= 14
597 then WriteByteDirect( (size shl 4) or getCompactType(elemType))
598 else begin
599 WriteByteDirect( $F0 or getCompactType(elemType));
600 WriteVarint32( Cardinal(size));
601 end;
602end;
603
604
605// Write an i64 as a varint. Results in 1-10 bytes on the wire.
606procedure TCompactProtocolImpl.WriteVarint64( n : UInt64);
Jens Geyerf726ae32021-06-04 11:17:26 +0200607var idx : Integer;
608 varint64out : packed array[0..9] of Byte;
Jens Geyerf0e63312015-03-01 18:47:49 +0100609begin
Jens Geyerf0e63312015-03-01 18:47:49 +0100610 idx := 0;
611 while TRUE do begin
612 ASSERT( idx < Length(varint64out));
613
614 // last one?
615 if (n and not UInt64($7F)) = 0 then begin
616 varint64out[idx] := Byte(n);
617 Inc(idx);
618 Break;
619 end;
620
621 varint64out[idx] := Byte((n and $7F) or $80);
622 Inc(idx);
623 n := n shr 7;
624 end;
625
Jens Geyerf726ae32021-06-04 11:17:26 +0200626 Transport.Write( @varint64out[0], 0, idx);
Jens Geyerf0e63312015-03-01 18:47:49 +0100627end;
628
629
630// Convert l into a zigzag Int64. This allows negative numbers to be
631// represented compactly as a varint.
632class function TCompactProtocolImpl.longToZigzag( const n : Int64) : UInt64;
633begin
634 // there is no arithmetic right shift in Delphi
635 if n >= 0
636 then result := UInt64(n shl 1)
637 else result := UInt64(n shl 1) xor $FFFFFFFFFFFFFFFF;
638end;
639
640
641// Convert n into a zigzag Integer. This allows negative numbers to be
642// represented compactly as a varint.
643class function TCompactProtocolImpl.intToZigZag( const n : Integer) : Cardinal;
644begin
645 // there is no arithmetic right shift in Delphi
646 if n >= 0
647 then result := Cardinal(n shl 1)
648 else result := Cardinal(n shl 1) xor $FFFFFFFF;
649end;
650
651
652// Convert a Int64 into 8 little-endian bytes in buf
Jens Geyerf726ae32021-06-04 11:17:26 +0200653class procedure TCompactProtocolImpl.fixedLongToBytes( const n : Int64; var buf : TEightBytesArray);
Jens Geyerf0e63312015-03-01 18:47:49 +0100654begin
Jens Geyerf726ae32021-06-04 11:17:26 +0200655 ASSERT( Length(buf) >= 8);
Jens Geyerf0e63312015-03-01 18:47:49 +0100656 buf[0] := Byte( n and $FF);
657 buf[1] := Byte((n shr 8) and $FF);
658 buf[2] := Byte((n shr 16) and $FF);
659 buf[3] := Byte((n shr 24) and $FF);
660 buf[4] := Byte((n shr 32) and $FF);
661 buf[5] := Byte((n shr 40) and $FF);
662 buf[6] := Byte((n shr 48) and $FF);
663 buf[7] := Byte((n shr 56) and $FF);
664end;
665
666
667
668// Read a message header.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200669function TCompactProtocolImpl.ReadMessageBegin : TThriftMessage;
Jens Geyerf0e63312015-03-01 18:47:49 +0100670var protocolId, versionAndType, version, type_ : Byte;
671 seqid : Integer;
672 msgNm : String;
673begin
674 Reset;
675
676 protocolId := Byte( ReadByte);
677 if (protocolId <> PROTOCOL_ID)
Jens Geyere0e32402016-04-20 21:50:48 +0200678 then raise TProtocolExceptionBadVersion.Create( 'Expected protocol id ' + IntToHex(PROTOCOL_ID,2)
679 + ' but got ' + IntToHex(protocolId,2));
Jens Geyerf0e63312015-03-01 18:47:49 +0100680
681 versionAndType := Byte( ReadByte);
682 version := Byte( versionAndType and VERSION_MASK);
683 if (version <> VERSION)
Jens Geyere0e32402016-04-20 21:50:48 +0200684 then raise TProtocolExceptionBadVersion.Create( 'Expected version ' +IntToStr(VERSION)
685 + ' but got ' + IntToStr(version));
Jens Geyerf0e63312015-03-01 18:47:49 +0100686
687 type_ := Byte( (versionAndType shr TYPE_SHIFT_AMOUNT) and TYPE_BITS);
688 seqid := Integer( ReadVarint32);
689 msgNm := ReadString;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200690 Init( result, msgNm, TMessageType(type_), seqid);
Jens Geyerf0e63312015-03-01 18:47:49 +0100691end;
692
693
694// Read a struct begin. There's nothing on the wire for this, but it is our
695// opportunity to push a new struct begin marker onto the field stack.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200696function TCompactProtocolImpl.ReadStructBegin: TThriftStruct;
Jens Geyerf0e63312015-03-01 18:47:49 +0100697begin
698 lastField_.Push( lastFieldId_);
699 lastFieldId_ := 0;
Jens Geyer17c3ad92017-09-05 20:31:27 +0200700 Init( result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100701end;
702
703
704// Doesn't actually consume any wire data, just removes the last field for
705// this struct from the field stack.
706procedure TCompactProtocolImpl.ReadStructEnd;
707begin
708 // consume the last field we Read off the wire.
709 lastFieldId_ := lastField_.Pop();
710end;
711
712
713// Read a field header off the wire.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200714function TCompactProtocolImpl.ReadFieldBegin: TThriftField;
Jens Geyerf0e63312015-03-01 18:47:49 +0100715var type_ : Byte;
Jens Geyera715f702019-08-28 22:56:13 +0200716 modifier : ShortInt;
717 fieldId : SmallInt;
Jens Geyerf0e63312015-03-01 18:47:49 +0100718begin
719 type_ := Byte( ReadByte);
720
721 // if it's a stop, then we can return immediately, as the struct is over.
722 if type_ = Byte(Types.STOP) then begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200723 Init( result, '', TType.Stop, 0);
Jens Geyerf0e63312015-03-01 18:47:49 +0100724 Exit;
725 end;
726
727 // mask off the 4 MSB of the type header. it could contain a field id delta.
728 modifier := ShortInt( (type_ and $F0) shr 4);
729 if (modifier = 0)
730 then fieldId := ReadI16 // not a delta. look ahead for the zigzag varint field id.
Jens Geyera715f702019-08-28 22:56:13 +0200731 else fieldId := SmallInt( lastFieldId_ + modifier); // add the delta to the last Read field id.
Jens Geyerf0e63312015-03-01 18:47:49 +0100732
Jens Geyer17c3ad92017-09-05 20:31:27 +0200733 Init( result, '', getTType(Byte(type_ and $0F)), fieldId);
Jens Geyerf0e63312015-03-01 18:47:49 +0100734
735 // if this happens to be a boolean field, the value is encoded in the type
736 // save the boolean value in a special instance variable.
737 if isBoolType(type_) then begin
738 if Byte(type_ and $0F) = Byte(Types.BOOLEAN_TRUE)
739 then boolValue_ := bool_true
740 else boolValue_ := bool_false;
741 end;
742
743 // push the new field onto the field stack so we can keep the deltas going.
744 lastFieldId_ := result.ID;
745end;
746
747
748// Read a map header off the wire. If the size is zero, skip Reading the key
749// and value type. This means that 0-length maps will yield TMaps without the
750// "correct" types.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200751function TCompactProtocolImpl.ReadMapBegin: TThriftMap;
Jens Geyerf0e63312015-03-01 18:47:49 +0100752var size : Integer;
753 keyAndValueType : Byte;
754 key, val : TType;
755begin
756 size := Integer( ReadVarint32);
757 if size = 0
758 then keyAndValueType := 0
759 else keyAndValueType := Byte( ReadByte);
760
761 key := getTType( Byte( keyAndValueType shr 4));
762 val := getTType( Byte( keyAndValueType and $F));
Jens Geyer17c3ad92017-09-05 20:31:27 +0200763 Init( result, key, val, size);
Jens Geyerf0e63312015-03-01 18:47:49 +0100764 ASSERT( (result.KeyType = key) and (result.ValueType = val));
Jens Geyer41f47af2019-11-09 23:24:52 +0100765 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100766end;
767
768
769// Read a list header off the wire. If the list size is 0-14, the size will
770// be packed into the element type header. If it's a longer list, the 4 MSB
771// of the element type header will be $F, and a varint will follow with the
772// true size.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200773function TCompactProtocolImpl.ReadListBegin: TThriftList;
Jens Geyerf0e63312015-03-01 18:47:49 +0100774var size_and_type : Byte;
775 size : Integer;
776 type_ : TType;
777begin
778 size_and_type := Byte( ReadByte);
779
780 size := (size_and_type shr 4) and $0F;
781 if (size = 15)
782 then size := Integer( ReadVarint32);
783
784 type_ := getTType( size_and_type);
Jens Geyer17c3ad92017-09-05 20:31:27 +0200785 Init( result, type_, size);
Jens Geyer41f47af2019-11-09 23:24:52 +0100786 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100787end;
788
789
790// Read a set header off the wire. If the set size is 0-14, the size will
791// be packed into the element type header. If it's a longer set, the 4 MSB
792// of the element type header will be $F, and a varint will follow with the
793// true size.
Jens Geyer17c3ad92017-09-05 20:31:27 +0200794function TCompactProtocolImpl.ReadSetBegin: TThriftSet;
Jens Geyerf0e63312015-03-01 18:47:49 +0100795var size_and_type : Byte;
796 size : Integer;
797 type_ : TType;
798begin
799 size_and_type := Byte( ReadByte);
800
801 size := (size_and_type shr 4) and $0F;
802 if (size = 15)
803 then size := Integer( ReadVarint32);
804
805 type_ := getTType( size_and_type);
Jens Geyer17c3ad92017-09-05 20:31:27 +0200806 Init( result, type_, size);
Jens Geyer41f47af2019-11-09 23:24:52 +0100807 CheckReadBytesAvailable(result);
Jens Geyerf0e63312015-03-01 18:47:49 +0100808end;
809
810
811// Read a boolean off the wire. If this is a boolean field, the value should
812// already have been Read during ReadFieldBegin, so we'll just consume the
813// pre-stored value. Otherwise, Read a byte.
814function TCompactProtocolImpl.ReadBool: Boolean;
815begin
816 if boolValue_ <> unused then begin
817 result := (boolValue_ = bool_true);
818 boolValue_ := unused;
819 Exit;
820 end;
821
822 result := (Byte(ReadByte) = Byte(Types.BOOLEAN_TRUE));
823end;
824
825
826// Read a single byte off the wire. Nothing interesting here.
827function TCompactProtocolImpl.ReadByte: ShortInt;
Jens Geyerf0e63312015-03-01 18:47:49 +0100828begin
Jens Geyer17c3ad92017-09-05 20:31:27 +0200829 Transport.ReadAll( @result, SizeOf(result), 0, 1);
Jens Geyerf0e63312015-03-01 18:47:49 +0100830end;
831
832
833// Read an i16 from the wire as a zigzag varint.
834function TCompactProtocolImpl.ReadI16: SmallInt;
835begin
836 result := SmallInt( zigzagToInt( ReadVarint32));
837end;
838
839
840// Read an i32 from the wire as a zigzag varint.
841function TCompactProtocolImpl.ReadI32: Integer;
842begin
843 result := zigzagToInt( ReadVarint32);
844end;
845
846
847// Read an i64 from the wire as a zigzag varint.
848function TCompactProtocolImpl.ReadI64: Int64;
849begin
850 result := zigzagToLong( ReadVarint64);
851end;
852
853
854// No magic here - just Read a double off the wire.
Jens Geyerf726ae32021-06-04 11:17:26 +0200855function TCompactProtocolImpl.ReadDouble : Double;
856var longBits : TEightBytesArray;
Jens Geyerf0e63312015-03-01 18:47:49 +0100857begin
Jens Geyerf726ae32021-06-04 11:17:26 +0200858 ASSERT( SizeOf(longBits) = SizeOf(result));
859 Transport.ReadAll( @longBits[0], SizeOf(longBits), 0, SizeOf(longBits));
Jens Geyerf0e63312015-03-01 18:47:49 +0100860 result := Int64BitsToDouble( bytesToLong( longBits));
861end;
862
863
864// Read a byte[] from the wire.
865function TCompactProtocolImpl.ReadBinary: TBytes;
866var length : Integer;
867begin
868 length := Integer( ReadVarint32);
Jens Geyer41f47af2019-11-09 23:24:52 +0100869 FTrans.CheckReadBytesAvailable(length);
Jens Geyerf0e63312015-03-01 18:47:49 +0100870 SetLength( result, length);
871 if (length > 0)
872 then Transport.ReadAll( result, 0, length);
873end;
874
Jens Geyer62445c12022-06-29 00:00:00 +0200875function TCompactProtocolImpl.ReadUuid: TGuid;
876var network : TGuid; // in network order (Big Endian)
877begin
878 ASSERT( SizeOf(result) = 16);
879 FTrans.ReadAll( @network, SizeOf(network), 0, SizeOf(network));
Jens Geyerf8f62782022-09-10 00:55:02 +0200880 result := GuidUtils.SwapByteOrder(network);
Jens Geyer62445c12022-06-29 00:00:00 +0200881end;
882
Jens Geyerf0e63312015-03-01 18:47:49 +0100883
884procedure TCompactProtocolImpl.ReadMessageEnd;
885begin
886 // nothing to do
887end;
888
889
890procedure TCompactProtocolImpl.ReadFieldEnd;
891begin
892 // nothing to do
893end;
894
895
896procedure TCompactProtocolImpl.ReadMapEnd;
897begin
898 // nothing to do
899end;
900
901
902procedure TCompactProtocolImpl.ReadListEnd;
903begin
904 // nothing to do
905end;
906
907
908procedure TCompactProtocolImpl.ReadSetEnd;
909begin
910 // nothing to do
911end;
912
913
914
915// Read an i32 from the wire as a varint. The MSB of each byte is set
916// if there is another byte to follow. This can Read up to 5 bytes.
917function TCompactProtocolImpl.ReadVarint32 : Cardinal;
918var shift : Integer;
919 b : Byte;
920begin
921 result := 0;
922 shift := 0;
923 while TRUE do begin
924 b := Byte( ReadByte);
925 result := result or (Cardinal(b and $7F) shl shift);
926 if ((b and $80) <> $80)
927 then Break;
928 Inc( shift, 7);
929 end;
930end;
931
932
933// Read an i64 from the wire as a proper varint. The MSB of each byte is set
934// if there is another byte to follow. This can Read up to 10 bytes.
935function TCompactProtocolImpl.ReadVarint64 : UInt64;
936var shift : Integer;
937 b : Byte;
938begin
939 result := 0;
940 shift := 0;
941 while TRUE do begin
942 b := Byte( ReadByte);
943 result := result or (UInt64(b and $7F) shl shift);
944 if ((b and $80) <> $80)
945 then Break;
946 Inc( shift, 7);
947 end;
948end;
949
950
951// Convert from zigzag Integer to Integer.
952class function TCompactProtocolImpl.zigzagToInt( const n : Cardinal ) : Integer;
953begin
954 result := Integer(n shr 1) xor (-Integer(n and 1));
955end;
956
957
958// Convert from zigzag Int64 to Int64.
959class function TCompactProtocolImpl.zigzagToLong( const n : UInt64) : Int64;
960begin
961 result := Int64(n shr 1) xor (-Int64(n and 1));
962end;
963
964
965// Note that it's important that the mask bytes are Int64 literals,
966// otherwise they'll default to ints, and when you shift an Integer left 56 bits,
967// you just get a messed up Integer.
Jens Geyerf726ae32021-06-04 11:17:26 +0200968class function TCompactProtocolImpl.bytesToLong( const bytes : TEightBytesArray) : Int64;
Jens Geyerf0e63312015-03-01 18:47:49 +0100969begin
970 ASSERT( Length(bytes) >= 8);
971 result := (Int64(bytes[7] and $FF) shl 56) or
972 (Int64(bytes[6] and $FF) shl 48) or
973 (Int64(bytes[5] and $FF) shl 40) or
974 (Int64(bytes[4] and $FF) shl 32) or
975 (Int64(bytes[3] and $FF) shl 24) or
976 (Int64(bytes[2] and $FF) shl 16) or
977 (Int64(bytes[1] and $FF) shl 8) or
978 (Int64(bytes[0] and $FF));
979end;
980
981
982class function TCompactProtocolImpl.isBoolType( const b : byte) : Boolean;
983var lowerNibble : Byte;
984begin
985 lowerNibble := b and $0f;
986 result := (Types(lowerNibble) in [Types.BOOLEAN_TRUE, Types.BOOLEAN_FALSE]);
987end;
988
989
990// Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
991class function TCompactProtocolImpl.getTType( const type_ : byte) : TType;
992var tct : Types;
993begin
994 tct := Types( type_ and $0F);
995 if tct in [Low(Types)..High(Types)]
996 then result := tcompactTypeToType[tct]
Jens Geyere0e32402016-04-20 21:50:48 +0200997 else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(tct)));
Jens Geyerf0e63312015-03-01 18:47:49 +0100998end;
999
1000
1001// Given a TType value, find the appropriate TCompactProtocol.Types constant.
1002class function TCompactProtocolImpl.getCompactType( const ttype : TType) : Byte;
1003begin
1004 if ttype in VALID_TTYPES
1005 then result := Byte( ttypeToCompactType[ttype])
Jens Geyere0e32402016-04-20 21:50:48 +02001006 else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(ttype)));
Jens Geyerf0e63312015-03-01 18:47:49 +01001007end;
1008
1009
Jens Geyer41f47af2019-11-09 23:24:52 +01001010function TCompactProtocolImpl.GetMinSerializedSize( const aType : TType) : Integer;
1011// Return the minimum number of bytes a type will consume on the wire
1012begin
1013 case aType of
1014 TType.Stop: result := 0;
1015 TType.Void: result := 0;
1016 TType.Bool_: result := SizeOf(Byte);
1017 TType.Byte_: result := SizeOf(Byte);
1018 TType.Double_: result := 8; // uses fixedLongToBytes() which always writes 8 bytes
1019 TType.I16: result := SizeOf(Byte);
1020 TType.I32: result := SizeOf(Byte);
1021 TType.I64: result := SizeOf(Byte);
1022 TType.String_: result := SizeOf(Byte); // string length
1023 TType.Struct: result := 0; // empty struct
1024 TType.Map: result := SizeOf(Byte); // element count
1025 TType.Set_: result := SizeOf(Byte); // element count
1026 TType.List: result := SizeOf(Byte); // element count
Jens Geyer62445c12022-06-29 00:00:00 +02001027 TType.Uuid: result := SizeOf(TGuid);
Jens Geyer41f47af2019-11-09 23:24:52 +01001028 else
1029 raise TTransportExceptionBadArgs.Create('Unhandled type code');
1030 end;
1031end;
1032
1033
1034
1035
1036
Jens Geyerf0e63312015-03-01 18:47:49 +01001037//--- unit tests -------------------------------------------
1038
1039{$IFDEF Debug}
1040procedure TestDoubleToInt64Bits;
1041
1042 procedure TestPair( const a : Double; const b : Int64);
1043 begin
1044 ASSERT( TCompactProtocolImpl.DoubleToInt64Bits(a) = b);
1045 ASSERT( TCompactProtocolImpl.Int64BitsToDouble(b) = a);
1046 end;
1047
1048begin
1049 TestPair( 1.0000000000000000E+000, Int64($3FF0000000000000));
1050 TestPair( 1.5000000000000000E+001, Int64($402E000000000000));
1051 TestPair( 2.5500000000000000E+002, Int64($406FE00000000000));
1052 TestPair( 4.2949672950000000E+009, Int64($41EFFFFFFFE00000));
1053 TestPair( 3.9062500000000000E-003, Int64($3F70000000000000));
1054 TestPair( 2.3283064365386963E-010, Int64($3DF0000000000000));
1055 TestPair( 1.2345678901230000E-300, Int64($01AA74FE1C1E7E45));
1056 TestPair( 1.2345678901234500E-150, Int64($20D02A36586DB4BB));
1057 TestPair( 1.2345678901234565E+000, Int64($3FF3C0CA428C59FA));
1058 TestPair( 1.2345678901234567E+000, Int64($3FF3C0CA428C59FB));
1059 TestPair( 1.2345678901234569E+000, Int64($3FF3C0CA428C59FC));
1060 TestPair( 1.2345678901234569E+150, Int64($5F182344CD3CDF9F));
1061 TestPair( 1.2345678901234569E+300, Int64($7E3D7EE8BCBBD352));
1062 TestPair( -1.7976931348623157E+308, Int64($FFEFFFFFFFFFFFFF));
1063 TestPair( 1.7976931348623157E+308, Int64($7FEFFFFFFFFFFFFF));
1064 TestPair( 4.9406564584124654E-324, Int64($0000000000000001));
1065 TestPair( 0.0000000000000000E+000, Int64($0000000000000000));
1066 TestPair( 4.94065645841247E-324, Int64($0000000000000001));
1067 TestPair( 3.2378592100206092E-319, Int64($000000000000FFFF));
1068 TestPair( 1.3906711615669959E-309, Int64($0000FFFFFFFFFFFF));
1069 TestPair( NegInfinity, Int64($FFF0000000000000));
1070 TestPair( Infinity, Int64($7FF0000000000000));
1071
1072 // NaN is special
1073 ASSERT( TCompactProtocolImpl.DoubleToInt64Bits( NaN) = Int64($FFF8000000000000));
1074 ASSERT( IsNan( TCompactProtocolImpl.Int64BitsToDouble( Int64($FFF8000000000000))));
1075end;
1076{$ENDIF}
1077
1078
1079{$IFDEF Debug}
1080procedure TestZigZag;
1081
1082 procedure Test32( const test : Integer);
1083 var zz : Cardinal;
1084 begin
1085 zz := TCompactProtocolImpl.intToZigZag(test);
1086 ASSERT( TCompactProtocolImpl.zigzagToInt(zz) = test, IntToStr(test));
1087 end;
1088
1089 procedure Test64( const test : Int64);
1090 var zz : UInt64;
1091 begin
1092 zz := TCompactProtocolImpl.longToZigzag(test);
1093 ASSERT( TCompactProtocolImpl.zigzagToLong(zz) = test, IntToStr(test));
1094 end;
1095
1096var i : Integer;
1097begin
1098 // protobuf testcases
Jens Geyerd6834402015-03-07 13:16:34 +01001099 ASSERT( TCompactProtocolImpl.intToZigZag(0) = 0, 'pb #1 to ZigZag');
1100 ASSERT( TCompactProtocolImpl.intToZigZag(-1) = 1, 'pb #2 to ZigZag');
1101 ASSERT( TCompactProtocolImpl.intToZigZag(1) = 2, 'pb #3 to ZigZag');
1102 ASSERT( TCompactProtocolImpl.intToZigZag(-2) = 3, 'pb #4 to ZigZag');
1103 ASSERT( TCompactProtocolImpl.intToZigZag(+2147483647) = 4294967294, 'pb #5 to ZigZag');
1104 ASSERT( TCompactProtocolImpl.intToZigZag(-2147483648) = 4294967295, 'pb #6 to ZigZag');
1105
1106 // protobuf testcases
1107 ASSERT( TCompactProtocolImpl.zigzagToInt(0) = 0, 'pb #1 from ZigZag');
1108 ASSERT( TCompactProtocolImpl.zigzagToInt(1) = -1, 'pb #2 from ZigZag');
1109 ASSERT( TCompactProtocolImpl.zigzagToInt(2) = 1, 'pb #3 from ZigZag');
1110 ASSERT( TCompactProtocolImpl.zigzagToInt(3) = -2, 'pb #4 from ZigZag');
1111 ASSERT( TCompactProtocolImpl.zigzagToInt(4294967294) = +2147483647, 'pb #5 from ZigZag');
1112 ASSERT( TCompactProtocolImpl.zigzagToInt(4294967295) = -2147483648, 'pb #6 from ZigZag');
Jens Geyerf0e63312015-03-01 18:47:49 +01001113
1114 // back and forth 32
1115 Test32( 0);
1116 for i := 0 to 30 do begin
1117 Test32( +(Integer(1) shl i));
1118 Test32( -(Integer(1) shl i));
1119 end;
1120 Test32( Integer($7FFFFFFF));
1121 Test32( Integer($80000000));
1122
1123 // back and forth 64
1124 Test64( 0);
1125 for i := 0 to 62 do begin
1126 Test64( +(Int64(1) shl i));
1127 Test64( -(Int64(1) shl i));
1128 end;
1129 Test64( Int64($7FFFFFFFFFFFFFFF));
1130 Test64( Int64($8000000000000000));
1131end;
1132{$ENDIF}
1133
1134
Jens Geyera6ea4442015-03-02 23:06:57 +01001135{$IFDEF Debug}
1136procedure TestLongBytes;
1137
1138 procedure Test( const test : Int64);
Jens Geyerf726ae32021-06-04 11:17:26 +02001139 var buf : TCompactProtocolImpl.TEightBytesArray;
Jens Geyera6ea4442015-03-02 23:06:57 +01001140 begin
1141 TCompactProtocolImpl.fixedLongToBytes( test, buf);
1142 ASSERT( TCompactProtocolImpl.bytesToLong( buf) = test, IntToStr(test));
1143 end;
1144
1145var i : Integer;
1146begin
1147 Test( 0);
1148 for i := 0 to 62 do begin
1149 Test( +(Int64(1) shl i));
1150 Test( -(Int64(1) shl i));
1151 end;
1152 Test( Int64($7FFFFFFFFFFFFFFF));
1153 Test( Int64($8000000000000000));
1154end;
1155{$ENDIF}
1156
1157
Jens Geyera9235802018-09-25 00:21:12 +02001158{$IFDEF Debug}
1159procedure UnitTest;
1160var w : WORD;
1161const FPU_CW_DENORMALIZED = $0002;
1162begin
1163 w := Get8087CW;
1164 try
1165 Set8087CW( w or FPU_CW_DENORMALIZED);
1166
1167 TestDoubleToInt64Bits;
1168 TestZigZag;
1169 TestLongBytes;
1170
1171 finally
1172 Set8087CW( w);
1173 end;
1174end;
1175{$ENDIF}
1176
1177
Jens Geyerf0e63312015-03-01 18:47:49 +01001178initialization
1179 {$IFDEF Debug}
Jens Geyera9235802018-09-25 00:21:12 +02001180 UnitTest;
Jens Geyerf0e63312015-03-01 18:47:49 +01001181 {$ENDIF}
1182
1183end.
1184