summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas
diff options
context:
space:
mode:
Diffstat (limited to 'src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas')
-rw-r--r--src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas1118
1 files changed, 1118 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas
new file mode 100644
index 000000000..07cab9a05
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/delphi/src/Thrift.Protocol.Compact.pas
@@ -0,0 +1,1118 @@
+(*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *)
+
+{$SCOPEDENUMS ON}
+
+unit Thrift.Protocol.Compact;
+
+interface
+
+uses
+ Classes,
+ SysUtils,
+ Math,
+ Generics.Collections,
+ Thrift.Transport,
+ Thrift.Protocol,
+ Thrift.Utils;
+
+type
+ ICompactProtocol = interface( IProtocol)
+ ['{C01927EC-021A-45F7-93B1-23D6A5420EDD}']
+ end;
+
+ // Compact protocol implementation for thrift.
+ // Adapted from the C# version.
+ TCompactProtocolImpl = class( TProtocolImpl, ICompactProtocol)
+ public
+ type
+ TFactory = class( TInterfacedObject, IProtocolFactory)
+ public
+ function GetProtocol( const trans: ITransport): IProtocol;
+ end;
+
+ private const
+
+ { TODO
+ static TStruct ANONYMOUS_STRUCT = new TStruct("");
+ static TField TSTOP = new TField("", TType.Stop, (short)0);
+ }
+
+ PROTOCOL_ID = Byte( $82);
+ VERSION = Byte( 1);
+ VERSION_MASK = Byte( $1F); // 0001 1111
+ TYPE_MASK = Byte( $E0); // 1110 0000
+ TYPE_BITS = Byte( $07); // 0000 0111
+ TYPE_SHIFT_AMOUNT = Byte( 5);
+
+ private type
+ // All of the on-wire type codes.
+ Types = (
+ STOP = $00,
+ BOOLEAN_TRUE = $01,
+ BOOLEAN_FALSE = $02,
+ BYTE_ = $03,
+ I16 = $04,
+ I32 = $05,
+ I64 = $06,
+ DOUBLE_ = $07,
+ BINARY = $08,
+ LIST = $09,
+ SET_ = $0A,
+ MAP = $0B,
+ STRUCT = $0C
+ );
+
+ private const
+ ttypeToCompactType : array[TType] of Types = (
+ Types.STOP, // Stop = 0,
+ Types(-1), // Void = 1,
+ Types.BOOLEAN_TRUE, // Bool_ = 2,
+ Types.BYTE_, // Byte_ = 3,
+ Types.DOUBLE_, // Double_ = 4,
+ Types(-5), // unused
+ Types.I16, // I16 = 6,
+ Types(-7), // unused
+ Types.I32, // I32 = 8,
+ Types(-9), // unused
+ Types.I64, // I64 = 10,
+ Types.BINARY, // String_ = 11,
+ Types.STRUCT, // Struct = 12,
+ Types.MAP, // Map = 13,
+ Types.SET_, // Set_ = 14,
+ Types.LIST // List = 15,
+ );
+
+ tcompactTypeToType : array[Types] of TType = (
+ TType.Stop, // STOP
+ TType.Bool_, // BOOLEAN_TRUE
+ TType.Bool_, // BOOLEAN_FALSE
+ TType.Byte_, // BYTE_
+ TType.I16, // I16
+ TType.I32, // I32
+ TType.I64, // I64
+ TType.Double_, // DOUBLE_
+ TType.String_, // BINARY
+ TType.List, // LIST
+ TType.Set_, // SET_
+ TType.Map, // MAP
+ TType.Struct // STRUCT
+ );
+
+ private
+ // Used to keep track of the last field for the current and previous structs,
+ // so we can do the delta stuff.
+ lastField_ : TStack<Integer>;
+ lastFieldId_ : Integer;
+
+ // If we encounter a boolean field begin, save the TField here so it can
+ // have the value incorporated.
+ private booleanField_ : TThriftField;
+
+ // If we Read a field header, and it's a boolean field, save the boolean
+ // value here so that ReadBool can use it.
+ private boolValue_ : ( unused, bool_true, bool_false);
+
+ public
+ constructor Create(const trans : ITransport);
+ destructor Destroy; override;
+
+ procedure Reset;
+
+ private
+ procedure WriteByteDirect( const b : Byte); overload;
+
+ // Writes a byte without any possibility of all that field header nonsense.
+ procedure WriteByteDirect( const n : Integer); overload;
+
+ // Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ // TODO: make a permanent buffer like WriteVarint64?
+ procedure WriteVarint32( n : Cardinal);
+
+ private
+ // The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
+ // of the type header. This is used specifically in the boolean field case.
+ procedure WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
+
+ public
+ procedure WriteMessageBegin( const msg: TThriftMessage); override;
+ procedure WriteMessageEnd; override;
+ procedure WriteStructBegin( const struc: TThriftStruct); override;
+ procedure WriteStructEnd; override;
+ procedure WriteFieldBegin( const field: TThriftField); override;
+ procedure WriteFieldEnd; override;
+ procedure WriteFieldStop; override;
+ procedure WriteMapBegin( const map: TThriftMap); override;
+ procedure WriteMapEnd; override;
+ procedure WriteListBegin( const list: TThriftList); override;
+ procedure WriteListEnd(); override;
+ procedure WriteSetBegin( const set_: TThriftSet ); override;
+ procedure WriteSetEnd(); override;
+ procedure WriteBool( b: Boolean); override;
+ procedure WriteByte( b: ShortInt); override;
+ procedure WriteI16( i16: SmallInt); override;
+ procedure WriteI32( i32: Integer); override;
+ procedure WriteI64( const i64: Int64); override;
+ procedure WriteDouble( const dub: Double); override;
+ procedure WriteBinary( const b: TBytes); overload; override;
+
+ private
+ class function DoubleToInt64Bits( const db : Double) : Int64;
+ class function Int64BitsToDouble( const i64 : Int64) : Double;
+
+ // Abstract method for writing the start of lists and sets. List and sets on
+ // the wire differ only by the type indicator.
+ procedure WriteCollectionBegin( const elemType : TType; size : Integer);
+
+ procedure WriteVarint64( n : UInt64);
+
+ // Convert l into a zigzag long. This allows negative numbers to be
+ // represented compactly as a varint.
+ class function longToZigzag( const n : Int64) : UInt64;
+
+ // Convert n into a zigzag int. This allows negative numbers to be
+ // represented compactly as a varint.
+ class function intToZigZag( const n : Integer) : Cardinal;
+
+ //Convert a Int64 into little-endian bytes in buf starting at off and going until off+7.
+ class procedure fixedLongToBytes( const n : Int64; var buf : TBytes);
+
+ public
+ function ReadMessageBegin: TThriftMessage; override;
+ procedure ReadMessageEnd(); override;
+ function ReadStructBegin: TThriftStruct; override;
+ procedure ReadStructEnd; override;
+ function ReadFieldBegin: TThriftField; override;
+ procedure ReadFieldEnd(); override;
+ function ReadMapBegin: TThriftMap; override;
+ procedure ReadMapEnd(); override;
+ function ReadListBegin: TThriftList; override;
+ procedure ReadListEnd(); override;
+ function ReadSetBegin: TThriftSet; override;
+ procedure ReadSetEnd(); override;
+ function ReadBool: Boolean; override;
+ function ReadByte: ShortInt; override;
+ function ReadI16: SmallInt; override;
+ function ReadI32: Integer; override;
+ function ReadI64: Int64; override;
+ function ReadDouble:Double; override;
+ function ReadBinary: TBytes; overload; override;
+
+ private
+ // Internal Reading methods
+
+ // Read an i32 from the wire as a varint. The MSB of each byte is set
+ // if there is another byte to follow. This can Read up to 5 bytes.
+ function ReadVarint32 : Cardinal;
+
+ // Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ // if there is another byte to follow. This can Read up to 10 bytes.
+ function ReadVarint64 : UInt64;
+
+
+ // encoding helpers
+
+ // Convert from zigzag Integer to Integer.
+ class function zigzagToInt( const n : Cardinal ) : Integer;
+
+ // Convert from zigzag Int64 to Int64.
+ class function zigzagToLong( const n : UInt64) : Int64;
+
+ // Note that it's important that the mask bytes are Int64 literals,
+ // otherwise they'll default to ints, and when you shift an Integer left 56 bits,
+ // you just get a messed up Integer.
+ class function bytesToLong( const bytes : TBytes) : Int64;
+
+ // type testing and converting
+ class function isBoolType( const b : byte) : Boolean;
+
+ // Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
+ class function getTType( const type_ : byte) : TType;
+
+ // Given a TType value, find the appropriate TCompactProtocol.Types constant.
+ class function getCompactType( const ttype : TType) : Byte;
+ end;
+
+
+implementation
+
+
+
+//--- TCompactProtocolImpl.TFactory ----------------------------------------
+
+
+function TCompactProtocolImpl.TFactory.GetProtocol( const trans: ITransport): IProtocol;
+begin
+ result := TCompactProtocolImpl.Create( trans);
+end;
+
+
+//--- TCompactProtocolImpl -------------------------------------------------
+
+
+constructor TCompactProtocolImpl.Create(const trans: ITransport);
+begin
+ inherited Create( trans);
+
+ lastFieldId_ := 0;
+ lastField_ := TStack<Integer>.Create;
+
+ Init( booleanField_, '', TType.Stop, 0);
+ boolValue_ := unused;
+end;
+
+
+destructor TCompactProtocolImpl.Destroy;
+begin
+ try
+ FreeAndNil( lastField_);
+ finally
+ inherited Destroy;
+ end;
+end;
+
+
+
+procedure TCompactProtocolImpl.Reset;
+begin
+ lastField_.Clear();
+ lastFieldId_ := 0;
+ Init( booleanField_, '', TType.Stop, 0);
+ boolValue_ := unused;
+end;
+
+
+// Writes a byte without any possibility of all that field header nonsense.
+// Used internally by other writing methods that know they need to Write a byte.
+procedure TCompactProtocolImpl.WriteByteDirect( const b : Byte);
+begin
+ Transport.Write( @b, SizeOf(b));
+end;
+
+
+// Writes a byte without any possibility of all that field header nonsense.
+procedure TCompactProtocolImpl.WriteByteDirect( const n : Integer);
+begin
+ WriteByteDirect( Byte(n));
+end;
+
+
+// Write an i32 as a varint. Results in 1-5 bytes on the wire.
+procedure TCompactProtocolImpl.WriteVarint32( n : Cardinal);
+var i32buf : TBytes;
+ idx : Integer;
+begin
+ SetLength( i32buf, 5);
+ idx := 0;
+ while TRUE do begin
+ ASSERT( idx < Length(i32buf));
+
+ // last part?
+ if ((n and not $7F) = 0) then begin
+ i32buf[idx] := Byte(n);
+ Inc(idx);
+ Break;
+ end;
+
+ i32buf[idx] := Byte((n and $7F) or $80);
+ Inc(idx);
+ n := n shr 7;
+ end;
+
+ Transport.Write( i32buf, 0, idx);
+end;
+
+
+// Write a message header to the wire. Compact Protocol messages contain the
+// protocol version so we can migrate forwards in the future if need be.
+procedure TCompactProtocolImpl.WriteMessageBegin( const msg: TThriftMessage);
+var versionAndType : Byte;
+begin
+ Reset;
+
+ versionAndType := Byte( VERSION and VERSION_MASK)
+ or Byte( (Cardinal(msg.Type_) shl TYPE_SHIFT_AMOUNT) and TYPE_MASK);
+
+ WriteByteDirect( PROTOCOL_ID);
+ WriteByteDirect( versionAndType);
+ WriteVarint32( Cardinal(msg.SeqID));
+ WriteString( msg.Name);
+end;
+
+
+// Write a struct begin. This doesn't actually put anything on the wire. We use it as an
+// opportunity to put special placeholder markers on the field stack so we can get the
+// field id deltas correct.
+procedure TCompactProtocolImpl.WriteStructBegin( const struc: TThriftStruct);
+begin
+ lastField_.Push(lastFieldId_);
+ lastFieldId_ := 0;
+end;
+
+
+// Write a struct end. This doesn't actually put anything on the wire. We use this as an
+// opportunity to pop the last field from the current struct off of the field stack.
+procedure TCompactProtocolImpl.WriteStructEnd;
+begin
+ lastFieldId_ := lastField_.Pop();
+end;
+
+
+// Write a field header containing the field id and field type. If the difference between the
+// current field id and the last one is small (< 15), then the field id will be encoded in
+// the 4 MSB as a delta. Otherwise, the field id will follow the type header as a zigzag varint.
+procedure TCompactProtocolImpl.WriteFieldBegin( const field: TThriftField);
+begin
+ case field.Type_ of
+ TType.Bool_ : booleanField_ := field; // we want to possibly include the value, so we'll wait.
+ else
+ WriteFieldBeginInternal(field, $FF);
+ end;
+end;
+
+
+// The workhorse of WriteFieldBegin. It has the option of doing a 'type override'
+// of the type header. This is used specifically in the boolean field case.
+procedure TCompactProtocolImpl.WriteFieldBeginInternal( const field : TThriftField; typeOverride : Byte);
+var typeToWrite : Byte;
+begin
+ // if there's a type override, use that.
+ if typeOverride = $FF
+ then typeToWrite := getCompactType( field.Type_)
+ else typeToWrite := typeOverride;
+
+ // check if we can use delta encoding for the field id
+ if (field.ID > lastFieldId_) and ((field.ID - lastFieldId_) <= 15)
+ then begin
+ // Write them together
+ WriteByteDirect( ((field.ID - lastFieldId_) shl 4) or typeToWrite);
+ end
+ else begin
+ // Write them separate
+ WriteByteDirect( typeToWrite);
+ WriteI16( field.ID);
+ end;
+
+ lastFieldId_ := field.ID;
+end;
+
+
+// Write the STOP symbol so we know there are no more fields in this struct.
+procedure TCompactProtocolImpl.WriteFieldStop;
+begin
+ WriteByteDirect( Byte( Types.STOP));
+end;
+
+
+// Write a map header. If the map is empty, omit the key and value type
+// headers, as we don't need any additional information to skip it.
+procedure TCompactProtocolImpl.WriteMapBegin( const map: TThriftMap);
+var key, val : Byte;
+begin
+ if (map.Count = 0)
+ then WriteByteDirect( 0)
+ else begin
+ WriteVarint32( Cardinal( map.Count));
+ key := getCompactType(map.KeyType);
+ val := getCompactType(map.ValueType);
+ WriteByteDirect( (key shl 4) or val);
+ end;
+end;
+
+
+// Write a list header.
+procedure TCompactProtocolImpl.WriteListBegin( const list: TThriftList);
+begin
+ WriteCollectionBegin( list.ElementType, list.Count);
+end;
+
+
+// Write a set header.
+procedure TCompactProtocolImpl.WriteSetBegin( const set_: TThriftSet );
+begin
+ WriteCollectionBegin( set_.ElementType, set_.Count);
+end;
+
+
+// Write a boolean value. Potentially, this could be a boolean field, in
+// which case the field header info isn't written yet. If so, decide what the
+// right type header is for the value and then Write the field header.
+// Otherwise, Write a single byte.
+procedure TCompactProtocolImpl.WriteBool( b: Boolean);
+var bt : Types;
+begin
+ if b
+ then bt := Types.BOOLEAN_TRUE
+ else bt := Types.BOOLEAN_FALSE;
+
+ if booleanField_.Type_ = TType.Bool_ then begin
+ // we haven't written the field header yet
+ WriteFieldBeginInternal( booleanField_, Byte(bt));
+ booleanField_.Type_ := TType.Stop;
+ end
+ else begin
+ // we're not part of a field, so just Write the value.
+ WriteByteDirect( Byte(bt));
+ end;
+end;
+
+
+// Write a byte. Nothing to see here!
+procedure TCompactProtocolImpl.WriteByte( b: ShortInt);
+begin
+ WriteByteDirect( Byte(b));
+end;
+
+
+// Write an I16 as a zigzag varint.
+procedure TCompactProtocolImpl.WriteI16( i16: SmallInt);
+begin
+ WriteVarint32( intToZigZag( i16));
+end;
+
+
+// Write an i32 as a zigzag varint.
+procedure TCompactProtocolImpl.WriteI32( i32: Integer);
+begin
+ WriteVarint32( intToZigZag( i32));
+end;
+
+
+// Write an i64 as a zigzag varint.
+procedure TCompactProtocolImpl.WriteI64( const i64: Int64);
+begin
+ WriteVarint64( longToZigzag( i64));
+end;
+
+
+class function TCompactProtocolImpl.DoubleToInt64Bits( const db : Double) : Int64;
+begin
+ ASSERT( SizeOf(db) = SizeOf(result));
+ Move( db, result, SizeOf(result));
+end;
+
+
+class function TCompactProtocolImpl.Int64BitsToDouble( const i64 : Int64) : Double;
+begin
+ ASSERT( SizeOf(i64) = SizeOf(result));
+ Move( i64, result, SizeOf(result));
+end;
+
+
+// Write a double to the wire as 8 bytes.
+procedure TCompactProtocolImpl.WriteDouble( const dub: Double);
+var data : TBytes;
+begin
+ fixedLongToBytes( DoubleToInt64Bits(dub), data);
+ Transport.Write( data);
+end;
+
+
+// Write a byte array, using a varint for the size.
+procedure TCompactProtocolImpl.WriteBinary( const b: TBytes);
+begin
+ WriteVarint32( Cardinal(Length(b)));
+ Transport.Write( b);
+end;
+
+procedure TCompactProtocolImpl.WriteMessageEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.WriteMapEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.WriteListEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.WriteSetEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.WriteFieldEnd;
+begin
+ // nothing to do
+end;
+
+
+// Abstract method for writing the start of lists and sets. List and sets on
+// the wire differ only by the type indicator.
+procedure TCompactProtocolImpl.WriteCollectionBegin( const elemType : TType; size : Integer);
+begin
+ if size <= 14
+ then WriteByteDirect( (size shl 4) or getCompactType(elemType))
+ else begin
+ WriteByteDirect( $F0 or getCompactType(elemType));
+ WriteVarint32( Cardinal(size));
+ end;
+end;
+
+
+// Write an i64 as a varint. Results in 1-10 bytes on the wire.
+procedure TCompactProtocolImpl.WriteVarint64( n : UInt64);
+var varint64out : TBytes;
+ idx : Integer;
+begin
+ SetLength( varint64out, 10);
+ idx := 0;
+ while TRUE do begin
+ ASSERT( idx < Length(varint64out));
+
+ // last one?
+ if (n and not UInt64($7F)) = 0 then begin
+ varint64out[idx] := Byte(n);
+ Inc(idx);
+ Break;
+ end;
+
+ varint64out[idx] := Byte((n and $7F) or $80);
+ Inc(idx);
+ n := n shr 7;
+ end;
+
+ Transport.Write( varint64out, 0, idx);
+end;
+
+
+// Convert l into a zigzag Int64. This allows negative numbers to be
+// represented compactly as a varint.
+class function TCompactProtocolImpl.longToZigzag( const n : Int64) : UInt64;
+begin
+ // there is no arithmetic right shift in Delphi
+ if n >= 0
+ then result := UInt64(n shl 1)
+ else result := UInt64(n shl 1) xor $FFFFFFFFFFFFFFFF;
+end;
+
+
+// Convert n into a zigzag Integer. This allows negative numbers to be
+// represented compactly as a varint.
+class function TCompactProtocolImpl.intToZigZag( const n : Integer) : Cardinal;
+begin
+ // there is no arithmetic right shift in Delphi
+ if n >= 0
+ then result := Cardinal(n shl 1)
+ else result := Cardinal(n shl 1) xor $FFFFFFFF;
+end;
+
+
+// Convert a Int64 into 8 little-endian bytes in buf
+class procedure TCompactProtocolImpl.fixedLongToBytes( const n : Int64; var buf : TBytes);
+begin
+ SetLength( buf, 8);
+ buf[0] := Byte( n and $FF);
+ buf[1] := Byte((n shr 8) and $FF);
+ buf[2] := Byte((n shr 16) and $FF);
+ buf[3] := Byte((n shr 24) and $FF);
+ buf[4] := Byte((n shr 32) and $FF);
+ buf[5] := Byte((n shr 40) and $FF);
+ buf[6] := Byte((n shr 48) and $FF);
+ buf[7] := Byte((n shr 56) and $FF);
+end;
+
+
+
+// Read a message header.
+function TCompactProtocolImpl.ReadMessageBegin : TThriftMessage;
+var protocolId, versionAndType, version, type_ : Byte;
+ seqid : Integer;
+ msgNm : String;
+begin
+ Reset;
+
+ protocolId := Byte( ReadByte);
+ if (protocolId <> PROTOCOL_ID)
+ then raise TProtocolExceptionBadVersion.Create( 'Expected protocol id ' + IntToHex(PROTOCOL_ID,2)
+ + ' but got ' + IntToHex(protocolId,2));
+
+ versionAndType := Byte( ReadByte);
+ version := Byte( versionAndType and VERSION_MASK);
+ if (version <> VERSION)
+ then raise TProtocolExceptionBadVersion.Create( 'Expected version ' +IntToStr(VERSION)
+ + ' but got ' + IntToStr(version));
+
+ type_ := Byte( (versionAndType shr TYPE_SHIFT_AMOUNT) and TYPE_BITS);
+ seqid := Integer( ReadVarint32);
+ msgNm := ReadString;
+ Init( result, msgNm, TMessageType(type_), seqid);
+end;
+
+
+// Read a struct begin. There's nothing on the wire for this, but it is our
+// opportunity to push a new struct begin marker onto the field stack.
+function TCompactProtocolImpl.ReadStructBegin: TThriftStruct;
+begin
+ lastField_.Push( lastFieldId_);
+ lastFieldId_ := 0;
+ Init( result);
+end;
+
+
+// Doesn't actually consume any wire data, just removes the last field for
+// this struct from the field stack.
+procedure TCompactProtocolImpl.ReadStructEnd;
+begin
+ // consume the last field we Read off the wire.
+ lastFieldId_ := lastField_.Pop();
+end;
+
+
+// Read a field header off the wire.
+function TCompactProtocolImpl.ReadFieldBegin: TThriftField;
+var type_ : Byte;
+ modifier : ShortInt;
+ fieldId : SmallInt;
+begin
+ type_ := Byte( ReadByte);
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if type_ = Byte(Types.STOP) then begin
+ Init( result, '', TType.Stop, 0);
+ Exit;
+ end;
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ modifier := ShortInt( (type_ and $F0) shr 4);
+ if (modifier = 0)
+ then fieldId := ReadI16 // not a delta. look ahead for the zigzag varint field id.
+ else fieldId := SmallInt( lastFieldId_ + modifier); // add the delta to the last Read field id.
+
+ Init( result, '', getTType(Byte(type_ and $0F)), fieldId);
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ // save the boolean value in a special instance variable.
+ if isBoolType(type_) then begin
+ if Byte(type_ and $0F) = Byte(Types.BOOLEAN_TRUE)
+ then boolValue_ := bool_true
+ else boolValue_ := bool_false;
+ end;
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ lastFieldId_ := result.ID;
+end;
+
+
+// Read a map header off the wire. If the size is zero, skip Reading the key
+// and value type. This means that 0-length maps will yield TMaps without the
+// "correct" types.
+function TCompactProtocolImpl.ReadMapBegin: TThriftMap;
+var size : Integer;
+ keyAndValueType : Byte;
+ key, val : TType;
+begin
+ size := Integer( ReadVarint32);
+ if size = 0
+ then keyAndValueType := 0
+ else keyAndValueType := Byte( ReadByte);
+
+ key := getTType( Byte( keyAndValueType shr 4));
+ val := getTType( Byte( keyAndValueType and $F));
+ Init( result, key, val, size);
+ ASSERT( (result.KeyType = key) and (result.ValueType = val));
+end;
+
+
+// Read a list header off the wire. If the list size is 0-14, the size will
+// be packed into the element type header. If it's a longer list, the 4 MSB
+// of the element type header will be $F, and a varint will follow with the
+// true size.
+function TCompactProtocolImpl.ReadListBegin: TThriftList;
+var size_and_type : Byte;
+ size : Integer;
+ type_ : TType;
+begin
+ size_and_type := Byte( ReadByte);
+
+ size := (size_and_type shr 4) and $0F;
+ if (size = 15)
+ then size := Integer( ReadVarint32);
+
+ type_ := getTType( size_and_type);
+ Init( result, type_, size);
+end;
+
+
+// Read a set header off the wire. If the set size is 0-14, the size will
+// be packed into the element type header. If it's a longer set, the 4 MSB
+// of the element type header will be $F, and a varint will follow with the
+// true size.
+function TCompactProtocolImpl.ReadSetBegin: TThriftSet;
+var size_and_type : Byte;
+ size : Integer;
+ type_ : TType;
+begin
+ size_and_type := Byte( ReadByte);
+
+ size := (size_and_type shr 4) and $0F;
+ if (size = 15)
+ then size := Integer( ReadVarint32);
+
+ type_ := getTType( size_and_type);
+ Init( result, type_, size);
+end;
+
+
+// Read a boolean off the wire. If this is a boolean field, the value should
+// already have been Read during ReadFieldBegin, so we'll just consume the
+// pre-stored value. Otherwise, Read a byte.
+function TCompactProtocolImpl.ReadBool: Boolean;
+begin
+ if boolValue_ <> unused then begin
+ result := (boolValue_ = bool_true);
+ boolValue_ := unused;
+ Exit;
+ end;
+
+ result := (Byte(ReadByte) = Byte(Types.BOOLEAN_TRUE));
+end;
+
+
+// Read a single byte off the wire. Nothing interesting here.
+function TCompactProtocolImpl.ReadByte: ShortInt;
+begin
+ Transport.ReadAll( @result, SizeOf(result), 0, 1);
+end;
+
+
+// Read an i16 from the wire as a zigzag varint.
+function TCompactProtocolImpl.ReadI16: SmallInt;
+begin
+ result := SmallInt( zigzagToInt( ReadVarint32));
+end;
+
+
+// Read an i32 from the wire as a zigzag varint.
+function TCompactProtocolImpl.ReadI32: Integer;
+begin
+ result := zigzagToInt( ReadVarint32);
+end;
+
+
+// Read an i64 from the wire as a zigzag varint.
+function TCompactProtocolImpl.ReadI64: Int64;
+begin
+ result := zigzagToLong( ReadVarint64);
+end;
+
+
+// No magic here - just Read a double off the wire.
+function TCompactProtocolImpl.ReadDouble:Double;
+var longBits : TBytes;
+begin
+ SetLength( longBits, 8);
+ Transport.ReadAll( longBits, 0, 8);
+ result := Int64BitsToDouble( bytesToLong( longBits));
+end;
+
+
+// Read a byte[] from the wire.
+function TCompactProtocolImpl.ReadBinary: TBytes;
+var length : Integer;
+begin
+ length := Integer( ReadVarint32);
+ SetLength( result, length);
+ if (length > 0)
+ then Transport.ReadAll( result, 0, length);
+end;
+
+
+procedure TCompactProtocolImpl.ReadMessageEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.ReadFieldEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.ReadMapEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.ReadListEnd;
+begin
+ // nothing to do
+end;
+
+
+procedure TCompactProtocolImpl.ReadSetEnd;
+begin
+ // nothing to do
+end;
+
+
+
+// Read an i32 from the wire as a varint. The MSB of each byte is set
+// if there is another byte to follow. This can Read up to 5 bytes.
+function TCompactProtocolImpl.ReadVarint32 : Cardinal;
+var shift : Integer;
+ b : Byte;
+begin
+ result := 0;
+ shift := 0;
+ while TRUE do begin
+ b := Byte( ReadByte);
+ result := result or (Cardinal(b and $7F) shl shift);
+ if ((b and $80) <> $80)
+ then Break;
+ Inc( shift, 7);
+ end;
+end;
+
+
+// Read an i64 from the wire as a proper varint. The MSB of each byte is set
+// if there is another byte to follow. This can Read up to 10 bytes.
+function TCompactProtocolImpl.ReadVarint64 : UInt64;
+var shift : Integer;
+ b : Byte;
+begin
+ result := 0;
+ shift := 0;
+ while TRUE do begin
+ b := Byte( ReadByte);
+ result := result or (UInt64(b and $7F) shl shift);
+ if ((b and $80) <> $80)
+ then Break;
+ Inc( shift, 7);
+ end;
+end;
+
+
+// Convert from zigzag Integer to Integer.
+class function TCompactProtocolImpl.zigzagToInt( const n : Cardinal ) : Integer;
+begin
+ result := Integer(n shr 1) xor (-Integer(n and 1));
+end;
+
+
+// Convert from zigzag Int64 to Int64.
+class function TCompactProtocolImpl.zigzagToLong( const n : UInt64) : Int64;
+begin
+ result := Int64(n shr 1) xor (-Int64(n and 1));
+end;
+
+
+// Note that it's important that the mask bytes are Int64 literals,
+// otherwise they'll default to ints, and when you shift an Integer left 56 bits,
+// you just get a messed up Integer.
+class function TCompactProtocolImpl.bytesToLong( const bytes : TBytes) : Int64;
+begin
+ ASSERT( Length(bytes) >= 8);
+ result := (Int64(bytes[7] and $FF) shl 56) or
+ (Int64(bytes[6] and $FF) shl 48) or
+ (Int64(bytes[5] and $FF) shl 40) or
+ (Int64(bytes[4] and $FF) shl 32) or
+ (Int64(bytes[3] and $FF) shl 24) or
+ (Int64(bytes[2] and $FF) shl 16) or
+ (Int64(bytes[1] and $FF) shl 8) or
+ (Int64(bytes[0] and $FF));
+end;
+
+
+class function TCompactProtocolImpl.isBoolType( const b : byte) : Boolean;
+var lowerNibble : Byte;
+begin
+ lowerNibble := b and $0f;
+ result := (Types(lowerNibble) in [Types.BOOLEAN_TRUE, Types.BOOLEAN_FALSE]);
+end;
+
+
+// Given a TCompactProtocol.Types constant, convert it to its corresponding TType value.
+class function TCompactProtocolImpl.getTType( const type_ : byte) : TType;
+var tct : Types;
+begin
+ tct := Types( type_ and $0F);
+ if tct in [Low(Types)..High(Types)]
+ then result := tcompactTypeToType[tct]
+ else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(tct)));
+end;
+
+
+// Given a TType value, find the appropriate TCompactProtocol.Types constant.
+class function TCompactProtocolImpl.getCompactType( const ttype : TType) : Byte;
+begin
+ if ttype in VALID_TTYPES
+ then result := Byte( ttypeToCompactType[ttype])
+ else raise TProtocolExceptionInvalidData.Create('don''t know what type: '+IntToStr(Ord(ttype)));
+end;
+
+
+//--- unit tests -------------------------------------------
+
+{$IFDEF Debug}
+procedure TestDoubleToInt64Bits;
+
+ procedure TestPair( const a : Double; const b : Int64);
+ begin
+ ASSERT( TCompactProtocolImpl.DoubleToInt64Bits(a) = b);
+ ASSERT( TCompactProtocolImpl.Int64BitsToDouble(b) = a);
+ end;
+
+begin
+ TestPair( 1.0000000000000000E+000, Int64($3FF0000000000000));
+ TestPair( 1.5000000000000000E+001, Int64($402E000000000000));
+ TestPair( 2.5500000000000000E+002, Int64($406FE00000000000));
+ TestPair( 4.2949672950000000E+009, Int64($41EFFFFFFFE00000));
+ TestPair( 3.9062500000000000E-003, Int64($3F70000000000000));
+ TestPair( 2.3283064365386963E-010, Int64($3DF0000000000000));
+ TestPair( 1.2345678901230000E-300, Int64($01AA74FE1C1E7E45));
+ TestPair( 1.2345678901234500E-150, Int64($20D02A36586DB4BB));
+ TestPair( 1.2345678901234565E+000, Int64($3FF3C0CA428C59FA));
+ TestPair( 1.2345678901234567E+000, Int64($3FF3C0CA428C59FB));
+ TestPair( 1.2345678901234569E+000, Int64($3FF3C0CA428C59FC));
+ TestPair( 1.2345678901234569E+150, Int64($5F182344CD3CDF9F));
+ TestPair( 1.2345678901234569E+300, Int64($7E3D7EE8BCBBD352));
+ TestPair( -1.7976931348623157E+308, Int64($FFEFFFFFFFFFFFFF));
+ TestPair( 1.7976931348623157E+308, Int64($7FEFFFFFFFFFFFFF));
+ TestPair( 4.9406564584124654E-324, Int64($0000000000000001));
+ TestPair( 0.0000000000000000E+000, Int64($0000000000000000));
+ TestPair( 4.94065645841247E-324, Int64($0000000000000001));
+ TestPair( 3.2378592100206092E-319, Int64($000000000000FFFF));
+ TestPair( 1.3906711615669959E-309, Int64($0000FFFFFFFFFFFF));
+ TestPair( NegInfinity, Int64($FFF0000000000000));
+ TestPair( Infinity, Int64($7FF0000000000000));
+
+ // NaN is special
+ ASSERT( TCompactProtocolImpl.DoubleToInt64Bits( NaN) = Int64($FFF8000000000000));
+ ASSERT( IsNan( TCompactProtocolImpl.Int64BitsToDouble( Int64($FFF8000000000000))));
+end;
+{$ENDIF}
+
+
+{$IFDEF Debug}
+procedure TestZigZag;
+
+ procedure Test32( const test : Integer);
+ var zz : Cardinal;
+ begin
+ zz := TCompactProtocolImpl.intToZigZag(test);
+ ASSERT( TCompactProtocolImpl.zigzagToInt(zz) = test, IntToStr(test));
+ end;
+
+ procedure Test64( const test : Int64);
+ var zz : UInt64;
+ begin
+ zz := TCompactProtocolImpl.longToZigzag(test);
+ ASSERT( TCompactProtocolImpl.zigzagToLong(zz) = test, IntToStr(test));
+ end;
+
+var i : Integer;
+begin
+ // protobuf testcases
+ ASSERT( TCompactProtocolImpl.intToZigZag(0) = 0, 'pb #1 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(-1) = 1, 'pb #2 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(1) = 2, 'pb #3 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(-2) = 3, 'pb #4 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(+2147483647) = 4294967294, 'pb #5 to ZigZag');
+ ASSERT( TCompactProtocolImpl.intToZigZag(-2147483648) = 4294967295, 'pb #6 to ZigZag');
+
+ // protobuf testcases
+ ASSERT( TCompactProtocolImpl.zigzagToInt(0) = 0, 'pb #1 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(1) = -1, 'pb #2 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(2) = 1, 'pb #3 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(3) = -2, 'pb #4 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(4294967294) = +2147483647, 'pb #5 from ZigZag');
+ ASSERT( TCompactProtocolImpl.zigzagToInt(4294967295) = -2147483648, 'pb #6 from ZigZag');
+
+ // back and forth 32
+ Test32( 0);
+ for i := 0 to 30 do begin
+ Test32( +(Integer(1) shl i));
+ Test32( -(Integer(1) shl i));
+ end;
+ Test32( Integer($7FFFFFFF));
+ Test32( Integer($80000000));
+
+ // back and forth 64
+ Test64( 0);
+ for i := 0 to 62 do begin
+ Test64( +(Int64(1) shl i));
+ Test64( -(Int64(1) shl i));
+ end;
+ Test64( Int64($7FFFFFFFFFFFFFFF));
+ Test64( Int64($8000000000000000));
+end;
+{$ENDIF}
+
+
+{$IFDEF Debug}
+procedure TestLongBytes;
+
+ procedure Test( const test : Int64);
+ var buf : TBytes;
+ begin
+ TCompactProtocolImpl.fixedLongToBytes( test, buf);
+ ASSERT( TCompactProtocolImpl.bytesToLong( buf) = test, IntToStr(test));
+ end;
+
+var i : Integer;
+begin
+ Test( 0);
+ for i := 0 to 62 do begin
+ Test( +(Int64(1) shl i));
+ Test( -(Int64(1) shl i));
+ end;
+ Test( Int64($7FFFFFFFFFFFFFFF));
+ Test( Int64($8000000000000000));
+end;
+{$ENDIF}
+
+
+{$IFDEF Debug}
+procedure UnitTest;
+var w : WORD;
+const FPU_CW_DENORMALIZED = $0002;
+begin
+ w := Get8087CW;
+ try
+ Set8087CW( w or FPU_CW_DENORMALIZED);
+
+ TestDoubleToInt64Bits;
+ TestZigZag;
+ TestLongBytes;
+
+ finally
+ Set8087CW( w);
+ end;
+end;
+{$ENDIF}
+
+
+initialization
+ {$IFDEF Debug}
+ UnitTest;
+ {$ENDIF}
+
+end.
+