diff --git a/lib/delphi/src/Thrift.Serializer.pas b/lib/delphi/src/Thrift.Serializer.pas index 8ee8a3540dc..c354a37dacc 100644 --- a/lib/delphi/src/Thrift.Serializer.pas +++ b/lib/delphi/src/Thrift.Serializer.pas @@ -66,7 +66,7 @@ TDeserializer = class public constructor Create( const aProtFact : IProtocolFactory = nil; // defaults to TBinaryProtocol const aTransFact : ITransportFactory = nil; - const aConfig : IThriftConfiguration = nil); + const aConfig : IThriftConfiguration = nil); // DTOR destructor Destroy; override; @@ -74,6 +74,11 @@ TDeserializer = class // Deserialize the Thrift object data. procedure Deserialize( const input : TBytes; const target : IBase); overload; procedure Deserialize( const input : TStream; const target : IBase); overload; + + // helper + property Protocol : IProtocol read FProtocol; + property Transport : ITransport read FTransport; + property Stream : TThriftMemoryStream read FStream; end; @@ -164,14 +169,14 @@ procedure TSerializer.Serialize( const input : IBase; const aStm : TStream); constructor TDeserializer.Create( const aProtFact : IProtocolFactory; const aTransFact : ITransportFactory; - const aConfig : IThriftConfiguration); + const aConfig : IThriftConfiguration); var adapter : IThriftStream; protfact : IProtocolFactory; begin inherited Create; - FStream := TThriftMemoryStream.Create; - adapter := TThriftStreamAdapterDelphi.Create( FStream, FALSE); + FStream := TThriftMemoryStream.Create; + adapter := TThriftStreamAdapterDelphi.Create( FStream, FALSE); FTransport := TStreamTransportImpl.Create( adapter, nil, aConfig); if aTransfact <> nil then FTransport := aTransfact.GetTransport( FTransport); @@ -205,8 +210,11 @@ procedure TDeserializer.Deserialize( const input : TBytes; const target : IBase) try iBytes := Length(input); FStream.Size := iBytes; - if iBytes > 0 - then Move( input[0], FStream.Memory^, iBytes); + if iBytes > 0 then begin + Move( input[0], FStream.Memory^, iBytes); + Transport.ResetMessageSizeAndConsumedBytes(); // size has changed + Transport.UpdateKnownMessageSize(iBytes); + end; target.Read( FProtocol); finally @@ -221,9 +229,15 @@ procedure TDeserializer.Deserialize( const input : TStream; const target : IBase var before : Int64; begin try - before := FStream.Position; - FStream.CopyFrom( input, COPY_ENTIRE_STREAM); - FStream.Position := before; + if Assigned(input) then begin + before := FStream.Position; + ASSERT( before = 0); + FStream.CopyFrom( input, COPY_ENTIRE_STREAM); + FStream.Position := before; + Transport.ResetMessageSizeAndConsumedBytes(); // size has changed + Transport.UpdateKnownMessageSize(FStream.Size); + end; + target.Read( FProtocol); finally FStream.Size := 0; // free any allocated memory diff --git a/lib/delphi/test/serializer/TestSerializer.Tests.pas b/lib/delphi/test/serializer/TestSerializer.Tests.pas index 466fb269efc..7f5b829ec51 100644 --- a/lib/delphi/test/serializer/TestSerializer.Tests.pas +++ b/lib/delphi/test/serializer/TestSerializer.Tests.pas @@ -49,7 +49,7 @@ interface type TFactoryPair = record - prot : IProtocolFactory; + proto : IProtocolFactory; trans : ITransportFactory; end; @@ -72,8 +72,9 @@ TTestSerializer = class //extends TestCase { class procedure Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair); overload; class procedure Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair); overload; - class procedure ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); overload; - class procedure ValidateReadToEnd( const input : TStream; const serial : TDeserializer); overload; + class procedure Deserialize( const input : TBytes; out target : TGuid; const factory : TFactoryPair); overload; + + class procedure ValidateReadToEnd( const serial : TDeserializer); overload; class function LengthOf( const bytes : TBytes) : Integer; overload; inline; class function LengthOf( const bytes : IThriftBytes) : Integer; overload; inline; @@ -90,6 +91,9 @@ TTestSerializer = class //extends TestCase { procedure Test_ExceptionStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); procedure Test_SimpleException( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); + procedure Test_ProtocolConformity( const factory : TFactoryPair; const stream : TFileStream); + procedure Test_UuidDeserialization( const factory : TFactoryPair; const stream : TFileStream); + public constructor Create; destructor Destroy; override; @@ -143,7 +147,7 @@ destructor TTestSerializer.Destroy; procedure TTestSerializer.AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory); var rec : TFactoryPair; begin - rec.prot := aProto; + rec.proto := aProto; rec.trans := aTrans; FProtocols.Add( rec); end; @@ -177,6 +181,53 @@ class function TTestSerializer.DataPtrOf( const bytes : IThriftBytes) : Pointer; end; +procedure TTestSerializer.Test_ProtocolConformity( const factory : TFactoryPair; const stream : TFileStream); +begin + Test_UuidDeserialization( factory, stream); + // add more tests here +end; + + +procedure TTestSerializer.Test_UuidDeserialization( const factory : TFactoryPair; const stream : TFileStream); + + function CreateGuidBytes : TBytes; + var obj : TObject; + i : Integer; + begin + obj := factory.proto as TObject; + + if obj is TJSONProtocolImpl.TFactory then begin + result := TEncoding.UTF8.GetBytes('"00112233-4455-6677-8899-aabbccddeeff"'); + Exit; + end; + + if (obj is TBinaryProtocolImpl.TFactory) + or (obj is TCompactProtocolImpl.TFactory) + then begin + SetLength(result,16); + for i := 0 to Length(result)-1 do result[i] := (i * $10) + i; + Exit; + end; + + raise Exception.Create('Unhandled case'); + end; + + +var tested, correct : TGuid; + bytes : TBytes; +begin + // write + bytes := CreateGuidBytes(); + + // init + read + Deserialize( bytes, tested, factory); + + // check + correct := TGuid.Create('{00112233-4455-6677-8899-aabbccddeeff}'); + ASSERT( tested = correct); +end; + + procedure TTestSerializer.Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); var tested, correct : IOneOfEach; bytes : TBytes; @@ -385,6 +436,11 @@ procedure TTestSerializer.Test_Serializer_Deserializer; for factory in FProtocols do begin Writeln('- '+UserFriendlyName(factory)); + // protocol conformity tests + if (method = TMethod.mt_Bytes) and (factory.trans = nil) + then Test_ProtocolConformity( factory, stream); + + // normal objects Test_OneOfEach( method, factory, stream); Test_CompactStruct( method, factory, stream); Test_ExceptionStruct( method, factory, stream); @@ -402,7 +458,7 @@ procedure TTestSerializer.Test_Serializer_Deserializer; class function TTestSerializer.UserFriendlyName( const factory : TFactoryPair) : string; begin - result := Copy( (factory.prot as TObject).ClassName, 2, MAXINT); + result := Copy( (factory.proto as TObject).ClassName, 2, MAXINT); if factory.trans <> nil then result := Copy( (factory.trans as TObject).ClassName, 2, MAXINT) +' '+ result; @@ -472,7 +528,7 @@ class function TTestSerializer.Serialize(const input : IBase; const factory : TF config := TThriftConfigurationImpl.Create; //config.MaxMessageSize := 0; // we don't read anything here - serial := TSerializer.Create( factory.prot, factory.trans, config); + serial := TSerializer.Create( factory.proto, factory.trans, config); try result := serial.Serialize( input); finally @@ -488,7 +544,7 @@ class procedure TTestSerializer.Serialize(const input : IBase; const factory : T config := TThriftConfigurationImpl.Create; //config.MaxMessageSize := 0; // we don't read anything here - serial := TSerializer.Create( factory.prot, factory.trans, config); + serial := TSerializer.Create( factory.proto, factory.trans, config); try serial.Serialize( input, aStream); finally @@ -504,10 +560,10 @@ class procedure TTestSerializer.Deserialize( const input : TBytes; const target config := TThriftConfigurationImpl.Create; config.MaxMessageSize := Length(input); - serial := TDeserializer.Create( factory.prot, factory.trans, config); + serial := TDeserializer.Create( factory.proto, factory.trans, config); try serial.Deserialize( input, target); - ValidateReadToEnd( input, serial); + ValidateReadToEnd( serial); finally serial.Free; end; @@ -521,44 +577,49 @@ class procedure TTestSerializer.Deserialize( const input : TStream; const target config := TThriftConfigurationImpl.Create; config.MaxMessageSize := input.Size; - serial := TDeserializer.Create( factory.prot, factory.trans, config); + serial := TDeserializer.Create( factory.proto, factory.trans, config); try serial.Deserialize( input, target); - ValidateReadToEnd( input, serial); + ValidateReadToEnd( serial); finally serial.Free; end; end; -class procedure TTestSerializer.ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); -// we should not have any more byte to read -var dummy : IBase; +class procedure TTestSerializer.Deserialize( const input : TBytes; out target : TGuid; const factory : TFactoryPair); +var serial : TDeserializer; + config : IThriftConfiguration; begin + config := TThriftConfigurationImpl.Create; + config.MaxMessageSize := Length(input); + + serial := TDeserializer.Create( factory.proto, factory.trans, config); try - dummy := TOneOfEachImpl.Create; - serial.Deserialize( input, dummy); - raise EInOutError.Create('Expected exception not thrown?'); - except - on e:TTransportExceptionEndOfFile do {expected}; - on e:Exception do raise; // unexpected + serial.Stream.Write(input[0], Length(input)); + serial.Stream.Position := 0; + serial.Transport.ResetMessageSizeAndConsumedBytes(); // size has changed + + target := serial.Protocol.ReadUuid; + finally + serial.Free; end; end; -class procedure TTestSerializer.ValidateReadToEnd( const input : TStream; const serial : TDeserializer); +class procedure TTestSerializer.ValidateReadToEnd( const serial : TDeserializer); // we should not have any more byte to read var dummy : IBase; begin try - input.Position := 0; dummy := TOneOfEachImpl.Create; - serial.Deserialize( input, dummy); + serial.Deserialize( nil, dummy); raise EInOutError.Create('Expected exception not thrown?'); except - on e:TTransportExceptionEndOfFile do {expected}; + on e:TTransportException do {expected}; on e:Exception do raise; // unexpected end; end; + end. diff --git a/lib/haxe/src/org/apache/thrift/TConfiguration.hx b/lib/haxe/src/org/apache/thrift/TConfiguration.hx index 47973f2c2c0..3bc5a864f8c 100644 --- a/lib/haxe/src/org/apache/thrift/TConfiguration.hx +++ b/lib/haxe/src/org/apache/thrift/TConfiguration.hx @@ -25,9 +25,9 @@ class TConfiguration public static inline var DEFAULT_MAX_FRAME_SIZE = 16384000; // this value is used consistently across all Thrift libraries public static inline var DEFAULT_RECURSION_DEPTH = 64; - public var MaxMessageSize(default,null) : Int = DEFAULT_MAX_MESSAGE_SIZE; - public var MaxFrameSize(default,null) : Int = DEFAULT_MAX_FRAME_SIZE; - public var RecursionLimit(default,null) : Int = DEFAULT_RECURSION_DEPTH; + public var MaxMessageSize(default,default) : Int = DEFAULT_MAX_MESSAGE_SIZE; + public var MaxFrameSize(default,default) : Int = DEFAULT_MAX_FRAME_SIZE; + public var RecursionLimit(default,default) : Int = DEFAULT_RECURSION_DEPTH; // TODO(JensG): add connection and i/o timeouts diff --git a/lib/haxe/src/org/apache/thrift/transport/TMemoryStream.hx b/lib/haxe/src/org/apache/thrift/transport/TMemoryStream.hx new file mode 100644 index 00000000000..49b494e64d2 --- /dev/null +++ b/lib/haxe/src/org/apache/thrift/transport/TMemoryStream.hx @@ -0,0 +1,88 @@ +/* + * 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. + */ + +package org.apache.thrift.transport; + +import haxe.io.Bytes; +import haxe.io.BytesBuffer; +import haxe.io.Output; + +class TMemoryStream implements TStream { + + private var Data : Bytes; + public var Position(default,default) : Int; + + public function new( data : Bytes = null) { + var target = new BytesBuffer(); + if ( data != null) { + for ( i in 0...data.length) { + target.addByte( data.get(i)); + ++Position; + } + } + Data = target.getBytes(); + } + + private function IsEof() : Bool { + return (0 > Position) || (Position >= Data.length); + } + + public function Close() : Void { + var target = new BytesBuffer(); + Data = target.getBytes(); + Position = 0; + } + + public function Peek() : Bool { + return (! IsEof()); + } + + // read count bytes into buf starting at offset + public function Read( buf : Bytes, offset : Int, count : Int) : Int { + var numRead = 0; + + for ( i in 0...count) { + if ( IsEof()) + break; + + buf.set( offset + i, Data.get( Position++)); + ++numRead; + } + + return numRead; + } + + // write count bytes from buf starting at offset + public function Write( buf : Bytes, offset : Int, count : Int) : Void { + var numBytes = buf.length - offset; + if ( numBytes > count) { + numBytes = count; + } + + for ( i in 0...numBytes) { + Data.set( Position + i, buf.get( offset + i)); + } + } + + public function Flush() : Void { + // nothing to do + } + +} + diff --git a/lib/haxe/test/HaxeTests.hxproj b/lib/haxe/test/HaxeTests.hxproj index 839917b4757..fbb9c8e5ef0 100644 --- a/lib/haxe/test/HaxeTests.hxproj +++ b/lib/haxe/test/HaxeTests.hxproj @@ -63,7 +63,7 @@ thrift -r -gen haxe ../../../lib/rb/benchmark/Benchmark.thrift