THRIFT-248. ruby: Factor BinaryProtocolAccelerated into separate protocol and struct components
This patch replaces the "binaryprotocolaccelerated" c extension with the "thrift_native" c extension. This new extension creates native implementations for the struct.rb #write and #read methods, Thrift::BinaryProtocol, and Thrift::MemoryBuffer, but keeps ruby-level interfaces, allowing all protocols to benefit from the struct code and the memory buffer. There is however an additional cost associated with going through this ruby layer, but the increased interoperability seems to be well worth it.
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@739895 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/rb/spec/binaryprotocol_spec_shared.rb b/lib/rb/spec/binaryprotocol_spec_shared.rb
index ced15b4..78e2ccb 100644
--- a/lib/rb/spec/binaryprotocol_spec_shared.rb
+++ b/lib/rb/spec/binaryprotocol_spec_shared.rb
@@ -2,7 +2,7 @@
shared_examples_for 'a binary protocol' do
before(:each) do
- @trans = mock("MockTransport")
+ @trans = Thrift::MemoryBuffer.new
@prot = protocol_class.new(@trans)
end
@@ -12,295 +12,241 @@
end
it "should write the message header" do
- @prot.should_receive(:write_i32).with(protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::CALL).ordered
- @prot.should_receive(:write_string).with('testMessage').ordered
- @prot.should_receive(:write_i32).with(17).ordered
@prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17)
+ @trans.read(1000).should == [protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::CALL, "testMessage".size, "testMessage", 17].pack("NNa11N")
end
# message footer is a noop
it "should write the field header" do
- @prot.should_receive(:write_byte).with(Thrift::Types::DOUBLE).ordered
- @prot.should_receive(:write_i16).with(3).ordered
@prot.write_field_begin('foo', Thrift::Types::DOUBLE, 3)
+ @trans.read(1000).should == [Thrift::Types::DOUBLE, 3].pack("cn")
end
-
+
# field footer is a noop
-
+
it "should write the STOP field" do
- @prot.should_receive(:write_byte).with(Thrift::Types::STOP)
@prot.write_field_stop
+ @trans.read(1).should == "\000"
end
-
+
it "should write the map header" do
- @prot.should_receive(:write_byte).with(Thrift::Types::STRING).ordered
- @prot.should_receive(:write_byte).with(Thrift::Types::LIST).ordered
- @prot.should_receive(:write_i32).with(17).ordered
@prot.write_map_begin(Thrift::Types::STRING, Thrift::Types::LIST, 17)
+ @trans.read(1000).should == [Thrift::Types::STRING, Thrift::Types::LIST, 17].pack("ccN");
end
-
+
# map footer is a noop
-
+
it "should write the list header" do
- @prot.should_receive(:write_byte).with(Thrift::Types::I16).ordered
- @prot.should_receive(:write_i32).with(42).ordered
@prot.write_list_begin(Thrift::Types::I16, 42)
+ @trans.read(1000).should == [Thrift::Types::I16, 42].pack("cN")
end
-
+
# list footer is a noop
-
+
it "should write the set header" do
- @prot.should_receive(:write_byte).with(Thrift::Types::BOOL).ordered
- @prot.should_receive(:write_i32).with(2).ordered
- @prot.write_set_begin(Thrift::Types::BOOL, 2)
+ @prot.write_set_begin(Thrift::Types::I16, 42)
+ @trans.read(1000).should == [Thrift::Types::I16, 42].pack("cN")
end
-
+
it "should write a bool" do
- @prot.should_receive(:write_byte).with(1).ordered
@prot.write_bool(true)
- @prot.should_receive(:write_byte).with(0).ordered
@prot.write_bool(false)
+ @trans.read(1000).should == "\001\000"
end
-
+
it "should treat a nil bool as false" do
- @prot.should_receive(:write_byte).with(0)
@prot.write_bool(nil)
+ @trans.read(1).should == "\000"
end
-
+
it "should write a byte" do
# byte is small enough, let's check -128..127
(-128..127).each do |i|
- @trans.should_receive(:write).with([i].pack('c')).ordered
@prot.write_byte(i)
+ @trans.read(1).should == [i].pack('c')
end
(-128..127).each do |i|
end
# handing it numbers out of signed range should clip
@trans.rspec_verify
(128..255).each do |i|
- @trans.should_receive(:write).with([i].pack('c')).ordered
@prot.write_byte(i)
+ @trans.read(1).should == [i].pack('c')
end
# and lastly, a Bignum is going to error out
lambda { @prot.write_byte(2**65) }.should raise_error(RangeError)
end
-
+
it "should error gracefully when trying to write a nil byte" do
lambda { @prot.write_byte(nil) }.should raise_error
end
-
+
it "should write an i16" do
# try a random scattering of values
# include the signed i16 minimum/maximum
- @trans.should_receive(:write).with("\200\000").ordered
- @trans.should_receive(:write).with("\374\000").ordered
- @trans.should_receive(:write).with("\000\021").ordered
- @trans.should_receive(:write).with("\000\000").ordered
- @trans.should_receive(:write).with("\330\360").ordered
- @trans.should_receive(:write).with("\006\273").ordered
- @trans.should_receive(:write).with("\177\377").ordered
[-2**15, -1024, 17, 0, -10000, 1723, 2**15-1].each do |i|
@prot.write_i16(i)
end
# and try something out of signed range, it should clip
- @trans.should_receive(:write).with("\200\005").ordered
@prot.write_i16(2**15 + 5)
+
+ @trans.read(1000).should == "\200\000\374\000\000\021\000\000\330\360\006\273\177\377\200\005"
+
# a Bignum should error
# lambda { @prot.write_i16(2**65) }.should raise_error(RangeError)
end
-
+
it "should error gracefully when trying to write a nil i16" do
lambda { @prot.write_i16(nil) }.should raise_error
end
-
+
it "should write an i32" do
# try a random scattering of values
# include the signed i32 minimum/maximum
- @trans.should_receive(:write).with("\200\000\000\000").ordered
- @trans.should_receive(:write).with("\377\376\037\r").ordered
- @trans.should_receive(:write).with("\377\377\366\034").ordered
- @trans.should_receive(:write).with("\377\377\377\375").ordered
- @trans.should_receive(:write).with("\000\000\000\000").ordered
- @trans.should_receive(:write).with("\000#\340\203").ordered
- @trans.should_receive(:write).with("\000\0000+").ordered
- @trans.should_receive(:write).with("\177\377\377\377").ordered
[-2**31, -123123, -2532, -3, 0, 2351235, 12331, 2**31-1].each do |i|
@prot.write_i32(i)
end
# try something out of signed range, it should clip
- @trans.should_receive(:write).with("\200\000\000\005").ordered
- @prot.write_i32(2 ** 31 + 5)
- # lambda { @prot.write_i32(2 ** 65 + 5) }.should raise_error(RangeError)
+ @trans.read(1000).should == "\200\000\000\000" + "\377\376\037\r" + "\377\377\366\034" + "\377\377\377\375" + "\000\000\000\000" + "\000#\340\203" + "\000\0000+" + "\177\377\377\377"
+ [2 ** 31 + 5, 2 ** 65 + 5].each do |i|
+ lambda { @prot.write_i32(i) }.should raise_error(RangeError)
+ end
end
-
+
it "should error gracefully when trying to write a nil i32" do
lambda { @prot.write_i32(nil) }.should raise_error
end
-
+
it "should write an i64" do
# try a random scattering of values
# try the signed i64 minimum/maximum
- @trans.should_receive(:write).with("\200\000\000\000\000\000\000\000").ordered
- @trans.should_receive(:write).with("\377\377\364\303\035\244+]").ordered
- @trans.should_receive(:write).with("\377\377\377\377\376\231:\341").ordered
- @trans.should_receive(:write).with("\377\377\377\377\377\377\377\026").ordered
- @trans.should_receive(:write).with("\000\000\000\000\000\000\000\000").ordered
- @trans.should_receive(:write).with("\000\000\000\000\000\000\004\317").ordered
- @trans.should_receive(:write).with("\000\000\000\000\000#\340\204").ordered
- @trans.should_receive(:write).with("\000\000\000\002\340\311~\365").ordered
- @trans.should_receive(:write).with("\177\377\377\377\377\377\377\377").ordered
[-2**63, -12356123612323, -23512351, -234, 0, 1231, 2351236, 12361236213, 2**63-1].each do |i|
@prot.write_i64(i)
end
# try something out of signed range, it should clip
- @trans.should_receive(:write).with("\200\000\000\000\000\000\000\005").ordered
- @prot.write_i64(2**63 + 5)
- # lambda { @prot.write_i64(2 ** 65 + 5) }.should raise_error(RangeError)
+ @trans.read(1000).should == ["\200\000\000\000\000\000\000\000",
+ "\377\377\364\303\035\244+]",
+ "\377\377\377\377\376\231:\341",
+ "\377\377\377\377\377\377\377\026",
+ "\000\000\000\000\000\000\000\000",
+ "\000\000\000\000\000\000\004\317",
+ "\000\000\000\000\000#\340\204",
+ "\000\000\000\002\340\311~\365",
+ "\177\377\377\377\377\377\377\377"].join("")
+ lambda { @prot.write_i64(2 ** 65 + 5) }.should raise_error(RangeError)
end
-
+
it "should error gracefully when trying to write a nil i64" do
lambda { @prot.write_i64(nil) }.should raise_error
end
-
+
it "should write a double" do
# try a random scattering of values, including min/max
- @trans.should_receive(:write).with([Float::MIN].pack('G')).ordered
- @trans.should_receive(:write).with("\300\223<\234\355\221hs").ordered
- @trans.should_receive(:write).with("\300\376\0173\256\024z\341").ordered
- @trans.should_receive(:write).with("\3007<2\336\372v\324").ordered
- @trans.should_receive(:write).with("\000\000\000\000\000\000\000\000").ordered
- @trans.should_receive(:write).with("@\310\037\220\365\302\217\\").ordered
- @trans.should_receive(:write).with("@\200Y\327\n=p\244").ordered
- @trans.should_receive(:write).with([Float::MAX].pack('G')).ordered
- [Float::MIN, -1231.15325, -123123.23, -23.23515123, 0, 12351.1325, 523.23, Float::MAX].each do |f|
+ values = [Float::MIN,-1231.15325, -123123.23, -23.23515123, 0, 12351.1325, 523.23, Float::MAX]
+ values.each do |f|
@prot.write_double(f)
+ @trans.read(1000).should == [f].pack("G")
end
end
-
+
it "should error gracefully when trying to write a nil double" do
lambda { @prot.write_double(nil) }.should raise_error
end
-
+
it "should write a string" do
str = "hello world"
- @prot.should_receive(:write_i32).with(str.length).ordered
- @trans.should_receive(:write).with(str).ordered
@prot.write_string(str)
+ @trans.read(1000).should == [str.size].pack("N") + str
end
-
+
it "should error gracefully when trying to write a nil string" do
lambda { @prot.write_string(nil) }.should raise_error
end
-
+
# message footer is a noop
-
+
it "should read a field header" do
- @prot.should_receive(:read_byte).ordered.and_return(Thrift::Types::STRING)
- @prot.should_receive(:read_i16).ordered.and_return(3)
+ @trans.write([Thrift::Types::STRING, 3].pack("cn"))
@prot.read_field_begin.should == [nil, Thrift::Types::STRING, 3]
end
-
+
# field footer is a noop
-
+
it "should read a stop field" do
- @prot.should_receive(:read_byte).and_return(Thrift::Types::STOP)
- @prot.should_not_receive(:read_i16)
+ @trans.write([Thrift::Types::STOP].pack("c"));
@prot.read_field_begin.should == [nil, Thrift::Types::STOP, 0]
end
it "should read a map header" do
- @prot.should_receive(:read_byte).and_return(Thrift::Types::DOUBLE, Thrift::Types::I64)
- @prot.should_receive(:read_i32).and_return(42)
+ @trans.write([Thrift::Types::DOUBLE, Thrift::Types::I64, 42].pack("ccN"))
@prot.read_map_begin.should == [Thrift::Types::DOUBLE, Thrift::Types::I64, 42]
end
-
+
# map footer is a noop
-
+
it "should read a list header" do
- @prot.should_receive(:read_byte).ordered.and_return(Thrift::Types::STRING)
- @prot.should_receive(:read_i32).and_return(17)
+ @trans.write([Thrift::Types::STRING, 17].pack("cN"))
@prot.read_list_begin.should == [Thrift::Types::STRING, 17]
end
-
+
# list footer is a noop
-
+
it "should read a set header" do
- @prot.should_receive(:read_byte).ordered.and_return(Thrift::Types::MAP)
- @prot.should_receive(:read_i32).ordered.and_return(42)
- @prot.read_set_begin.should == [Thrift::Types::MAP, 42]
+ @trans.write([Thrift::Types::STRING, 17].pack("cN"))
+ @prot.read_set_begin.should == [Thrift::Types::STRING, 17]
end
-
+
# set footer is a noop
-
+
it "should read a bool" do
- @prot.should_receive(:read_byte).and_return(1, 0)
+ @trans.write("\001\000");
@prot.read_bool.should == true
@prot.read_bool.should == false
end
-
+
it "should read a byte" do
- # try a scattering of values, including min/max
- @trans.should_receive(:read_all).with(1).and_return(
- "\200", "\307", "\375",
- "\000", "\021", "\030", "\177"
- )
[-128, -57, -3, 0, 17, 24, 127].each do |i|
+ @trans.write([i].pack("c"))
@prot.read_byte.should == i
end
end
-
+
it "should read an i16" do
# try a scattering of values, including min/max
- @trans.should_receive(:read_all).with(2).and_return(
- "\200\000", "\353\213", "\376\237",
- "\000\000", "\005\367", "\b\272", "\177\377"
- )
[-2**15, -5237, -353, 0, 1527, 2234, 2**15-1].each do |i|
+ @trans.write([i].pack("n"));
@prot.read_i16.should == i
end
end
-
+
it "should read an i32" do
# try a scattering of values, including min/max
- @trans.should_receive(:read_all).with(4).and_return(
- "\200\000\000\000", "\377\374i\213", "\377\377\347\244",
- "\000\000\000\000", "\000\000\t/", "\000\001\340\363", "\177\377\377\377"
- )
[-2**31, -235125, -6236, 0, 2351, 123123, 2**31-1].each do |i|
+ @trans.write([i].pack("N"))
@prot.read_i32.should == i
end
end
-
+
it "should read an i64" do
# try a scattering of values, including min/max
- @trans.should_receive(:read_all).with(8).and_return(
- "\200\000\000\000\000\000\000\000", "\377\377\377\377\370\243Z\b",
- "\377\377\377\377\377\377\3476", "\000\000\000\000\000\000\000\000",
- "\000\000\000\000\000\000\000 ", "\000\000\000\000\213\332\t\223",
- "\177\377\377\377\377\377\377\377"
- )
[-2**63, -123512312, -6346, 0, 32, 2346322323, 2**63-1].each do |i|
+ @trans.write([i >> 32, i & 0xFFFFFFFF].pack("NN"))
@prot.read_i64.should == i
end
end
-
+
it "should read a double" do
# try a random scattering of values, including min/max
- @trans.should_receive(:read_all).with(8).and_return(
- [Float::MIN].pack('G'), "\301\f9\370\374\362\317\226",
- "\300t3\274x \243\016", "\000\000\000\000\000\000\000\000", "@^\317\fCo\301Y",
- "AA\360A\217\317@\260", [Float::MAX].pack('G')
- )
[Float::MIN, -231231.12351, -323.233513, 0, 123.2351235, 2351235.12351235, Float::MAX].each do |f|
+ @trans.write([f].pack("G"));
@prot.read_double.should == f
end
end
-
+
it "should read a string" do
str = "hello world"
- @prot.should_receive(:read_i32).and_return(str.length)
- @trans.should_receive(:read_all).with(str.length).and_return(str)
+ @trans.write([str.size].pack("N") + str)
@prot.read_string.should == str
end
end