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
diff --git a/lib/rb/spec/binaryprotocolaccelerated_spec.rb b/lib/rb/spec/binaryprotocolaccelerated_spec.rb
index 6a3cc39..a2ddc55 100644
--- a/lib/rb/spec/binaryprotocolaccelerated_spec.rb
+++ b/lib/rb/spec/binaryprotocolaccelerated_spec.rb
@@ -6,133 +6,91 @@
 class ThriftBinaryProtocolAcceleratedSpec < Spec::ExampleGroup
   include Thrift
 
-  describe BinaryProtocolAccelerated do
-    # given that BinaryProtocolAccelerated only actually overrides read_message_begin
-    # this shared spec isn't going to do much, but it's still worth including
-    # for future-proofing in case we start overriding individual methods
+  describe Thrift::BinaryProtocolAccelerated do
+    # since BinaryProtocolAccelerated should be directly equivalent to 
+    # BinaryProtocol, we don't need any custom specs!
     it_should_behave_like 'a binary protocol'
 
     def protocol_class
       BinaryProtocolAccelerated
     end
 
-    before(:each) do
-      @buffer = ""
-      @trans.stub!(:borrow).and_return { @buffer }
-      @trans.stub!(:consume!).and_return do |*args|
-        n = args.first || 0
-        @buffer.slice!(0,n)
-      end
-    end
-
-    it "should read a message header" do
-      @buffer = "\200\001\000\002\000\000\000\vtestMessage\000\000\000*"
-      # @prot.should_receive(:read_i32).and_return(protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::REPLY, 42)
-      # @prot.should_receive(:read_string).and_return('testMessage')
-      @prot.read_message_begin.should == ['testMessage', Thrift::MessageTypes::REPLY, 42]
-    end
-
-    it "should raise an exception if the message header has the wrong version" do
-      @buffer = "\000\000\000\v"
-      # @prot.should_receive(:read_i32).and_return(42)
-      lambda { @prot.read_message_begin }.should raise_error(Thrift::ProtocolException, 'Missing version identifier') do |e|
-        e.type == Thrift::ProtocolException::BAD_VERSION
-      end
-    end
-
-    it "should encode a struct with all fields set identically to Thrift::BinaryProtocol" do
-      foo = SpecNamespace::Foo.new(:complex => {5 => {"foo" => 1.2}, 17 => {"bar" => 3.14159, "baz" => 5.8}})
-      @prot.encode_binary(foo).should == "\r\000\005\b\r\000\000\000\002\000\000\000\005\v\004\000\000\000\001\000\
-\000\000\003foo?\363333333\000\000\000\021\v\004\000\000\000\002\000\000\000\003baz@\027333333\000\000\000\003bar@\
-\t!\371\360\e\206n\016\000\006\006\000\000\000\003\000\005\000\021\000\357\b\000\001\000\000\0005\v\000\002\000\000\
-\000\005words\f\000\003\v\000\001\000\000\000\rhello, world!\000\017\000\004\b\000\000\000\004\000\000\000\001\000\
-\000\000\002\000\000\000\002\000\000\000\003\000"
-    end
-
-    it "should encode a struct with missing fields identically to Thrift::BinaryProtocol" do
-      foo = SpecNamespace::Foo.new(:simple => nil, :ints => nil)
-      @prot.encode_binary(foo).should == "\016\000\006\006\000\000\000\003\000\005\000\021\000\357\v\000\002\000\000\
-\000\005words\f\000\003\v\000\001\000\000\000\rhello, world!\000\000"
-    end
-
-    it "should decode a struct with all fields set identically to Thrift::BinaryProtocol" do
-      foo = SpecNamespace::Foo.new(:complex => {5 => {"foo" => 1.2}, 17 => {"bar" => 3.14159, "baz" => 5.8}})
-      trans = Thrift::MemoryBuffer.new("\r\000\005\b\r\000\000\000\002\000\000\000\005\v\004\000\000\000\001\000\
-\000\000\003foo?\363333333\000\000\000\021\v\004\000\000\000\002\000\000\000\003baz@\027333333\000\000\000\003bar@\
-\t!\371\360\e\206n\016\000\006\006\000\000\000\003\000\005\000\021\000\357\b\000\001\000\000\0005\v\000\002\000\000\
-\000\005words\f\000\003\v\000\001\000\000\000\rhello, world!\000\017\000\004\b\000\000\000\004\000\000\000\001\000\
-\000\000\002\000\000\000\002\000\000\000\003\000")
-      @prot.decode_binary(SpecNamespace::Foo.new, trans).should == foo
-    end
-
-    it "should decode a struct with missing fields identically to Thrift::BinaryProtocol" do
-      trans = Thrift::MemoryBuffer.new("\016\000\006\006\000\000\000\003\000\005\000\021\000\357\v\000\002\000\000\
-\000\005words\f\000\003\v\000\001\000\000\000\rhello, world!\000\000")
-      @prot.decode_binary(SpecNamespace::Foo.new, trans).should == SpecNamespace::Foo.new
-    end
-
-    it "should encode a string with null bytes in it" do
-      foo = SpecNamespace::Hello.new(:greeting => "Hello\000World!")
-      @prot.encode_binary(foo).should == "\v\000\001\000\000\000\fHello\000World!\000"
-    end
-
-    it "should decode a string with null bytes in it" do
-      trans = Thrift::MemoryBuffer.new("\v\000\001\000\000\000\fHello\000World!\000")
-      @prot.decode_binary(SpecNamespace::Hello.new, trans).should == SpecNamespace::Hello.new(:greeting => "Hello\000World!")
-    end
-
-    it "should error when encoding a struct with a nil value in a list" do
-      Thrift.type_checking = false
-      sl = SpecNamespace::SimpleList
-      hello = SpecNamespace::Hello
-      # nil counts as false for bools
-      # lambda { @prot.encode_binary(sl.new(:bools => [true, false, nil, false])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:bytes => [1, 2, nil, 3])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:i16s => [1, 2, nil, 3])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:i32s => [1, 2, nil, 3])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:i64s => [1, 2, nil, 3])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:doubles => [1.0, 2.0, nil, 3.0])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:strings => ["one", "two", nil, "three"])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:lists => [[1, 2], nil, [3, 4]])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:maps => [{1 => 2}, nil, {3 => 4}])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:sets => [Set.new([1, 2]), nil, Set.new([3, 4])])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:structs => [hello.new, nil, hello.new(:greeting => "hi")])) }.should raise_error
-    end
-
-    it "should error when encoding a non-nil, non-correctly-typed value in a list" do
-      Thrift.type_checking = false
-      sl = SpecNamespace::SimpleList
-      hello = SpecNamespace::Hello
-      # bool should accept any value
-      # lambda { @prot.encode_binary(sl.new(:bools => [true, false, 3])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:bytes => [1, 2, "3", 5])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:i16s => ["one", 2, 3])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:i32s => [[1,2], 3, 4])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:i64s => [{1 => 2}, 3, 4])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:doubles => ["one", 2.3, 3.4])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:strings => ["one", "two", 3, 4])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:lists => [{1 => 2}, [3, 4]])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:maps => [{1 => 2}, [3, 4]])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:sets => [Set.new([1, 2]), 3, 4])) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:structs => [3, "four"])) }.should raise_error
-    end
-
-    it "should error when given nil to encode" do
-      lambda { @prot.encode_binary(nil) }.should raise_error
-    end
-
-    it "should error when encoding an improper object where a container is expected" do
-      Thrift.type_checking = false
-      sl = SpecNamespace::SimpleList
-      lambda { @prot.encode_binary(sl.new(:strings => {"one" => "two", nil => "three"})) }.should raise_error
-      lambda { @prot.encode_binary(sl.new(:maps => [[1, 2]])) }.should raise_error
-    end
-
-    it "should accept arrays and hashes as sets" do
-      Thrift.type_checking = false
-      sl = SpecNamespace::SimpleList
-      lambda { @prot.encode_binary(sl.new(:sets => [[1, 2], {3 => true, 4 => true}])) }.should_not raise_error
-    end
+#     before(:each) do
+#       @buffer = ""
+#       @trans.stub!(:borrow).and_return { @buffer }
+#       @trans.stub!(:consume!).and_return do |*args|
+#         n = args.first || 0
+#         @buffer.slice!(0,n)
+#       end
+#     end
+# 
+# 
+#     it "should raise an exception if the message header has the wrong version" do
+#       @buffer = "\000\000\000\v"
+#       # @prot.should_receive(:read_i32).and_return(42)
+#       lambda { @prot.read_message_begin }.should raise_error(Thrift::ProtocolException, 'Missing version identifier') do |e|
+#         e.type == Thrift::ProtocolException::BAD_VERSION
+#       end
+#     end
+# 
+#     it "should encode a string with null bytes in it" do
+#       foo = SpecNamespace::Hello.new(:greeting => "Hello\000World!")
+#       @prot.encode_binary(foo).should == "\v\000\001\000\000\000\fHello\000World!\000"
+#     end
+# 
+#     it "should decode a string with null bytes in it" do
+#       trans = Thrift::MemoryBuffer.new("\v\000\001\000\000\000\fHello\000World!\000")
+#       @prot.decode_binary(SpecNamespace::Hello.new, trans).should == SpecNamespace::Hello.new(:greeting => "Hello\000World!")
+#     end
+# 
+#     it "should error when encoding a struct with a nil value in a list" do
+#       Thrift.type_checking = false
+#       sl = SpecNamespace::SimpleList
+#       hello = SpecNamespace::Hello
+#       # nil counts as false for bools
+#       # lambda { @prot.encode_binary(sl.new(:bools => [true, false, nil, false])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:bytes => [1, 2, nil, 3])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:i16s => [1, 2, nil, 3])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:i32s => [1, 2, nil, 3])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:i64s => [1, 2, nil, 3])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:doubles => [1.0, 2.0, nil, 3.0])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:strings => ["one", "two", nil, "three"])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:lists => [[1, 2], nil, [3, 4]])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:maps => [{1 => 2}, nil, {3 => 4}])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:sets => [Set.new([1, 2]), nil, Set.new([3, 4])])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:structs => [hello.new, nil, hello.new(:greeting => "hi")])) }.should raise_error
+#     end
+# 
+#     it "should error when encoding a non-nil, non-correctly-typed value in a list" do
+#       Thrift.type_checking = false
+#       sl = SpecNamespace::SimpleList
+#       hello = SpecNamespace::Hello
+#       # bool should accept any value
+#       # lambda { @prot.encode_binary(sl.new(:bools => [true, false, 3])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:bytes => [1, 2, "3", 5])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:i16s => ["one", 2, 3])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:i32s => [[1,2], 3, 4])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:i64s => [{1 => 2}, 3, 4])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:doubles => ["one", 2.3, 3.4])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:strings => ["one", "two", 3, 4])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:lists => [{1 => 2}, [3, 4]])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:maps => [{1 => 2}, [3, 4]])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:sets => [Set.new([1, 2]), 3, 4])) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:structs => [3, "four"])) }.should raise_error
+#     end
+# 
+#     it "should error when encoding an improper object where a container is expected" do
+#       Thrift.type_checking = false
+#       sl = SpecNamespace::SimpleList
+#       lambda { @prot.encode_binary(sl.new(:strings => {"one" => "two", nil => "three"})) }.should raise_error
+#       lambda { @prot.encode_binary(sl.new(:maps => [[1, 2]])) }.should raise_error
+#     end
+# 
+#     it "should accept arrays and hashes as sets" do
+#       Thrift.type_checking = false
+#       sl = SpecNamespace::SimpleList
+#       lambda { @prot.encode_binary(sl.new(:sets => [[1, 2], {3 => true, 4 => true}])) }.should_not raise_error
+#     end
   end
 
   describe BinaryProtocolAcceleratedFactory do
diff --git a/lib/rb/spec/protocol_spec.rb b/lib/rb/spec/protocol_spec.rb
index 917ca45..a681cd8 100644
--- a/lib/rb/spec/protocol_spec.rb
+++ b/lib/rb/spec/protocol_spec.rb
@@ -1,4 +1,5 @@
 require File.dirname(__FILE__) + '/spec_helper'
+require "thrift_native"
 
 class ThriftProtocolSpec < Spec::ExampleGroup
   include Thrift
@@ -94,18 +95,23 @@
         ['field 3', Types::MAP, 3],
         [nil, Types::STOP, 0]
       )
-      @prot.should_receive(:skip).with(Types::STRING).ordered
-      @prot.should_receive(:skip).with(Types::I32).ordered
-      @prot.should_receive(:skip).with(Types::MAP).ordered
       @prot.should_receive(:read_field_end).exactly(3).times
+      @prot.should_receive(:read_string).exactly(3).times
+      @prot.should_receive(:read_i32).ordered
+      @prot.should_receive(:read_map_begin).ordered.and_return([Types::STRING, Types::STRING, 1])
+      # @prot.should_receive(:read_string).exactly(2).times
+      @prot.should_receive(:read_map_end).ordered
       @prot.should_receive(:read_struct_end).ordered
       real_skip.call(Types::STRUCT)
     end
 
     it "should skip maps" do
       real_skip = @prot.method(:skip)
-      @prot.should_receive(:read_map_begin).ordered.and_return([Types::STRING, Types::STRUCT, 7])
-      @prot.should_receive(:skip).ordered.exactly(14).times # once per key and once per value
+      @prot.should_receive(:read_map_begin).ordered.and_return([Types::STRING, Types::STRUCT, 1])
+      @prot.should_receive(:read_string).ordered
+      @prot.should_receive(:read_struct_begin).ordered.and_return(["some_struct"])
+      @prot.should_receive(:read_field_begin).ordered.and_return([nil, Types::STOP, nil]);
+      @prot.should_receive(:read_struct_end).ordered
       @prot.should_receive(:read_map_end).ordered
       real_skip.call(Types::MAP)
     end
@@ -113,7 +119,7 @@
     it "should skip sets" do
       real_skip = @prot.method(:skip)
       @prot.should_receive(:read_set_begin).ordered.and_return([Types::I64, 9])
-      @prot.should_receive(:skip).with(Types::I64).ordered.exactly(9).times
+      @prot.should_receive(:read_i64).ordered.exactly(9).times
       @prot.should_receive(:read_set_end)
       real_skip.call(Types::SET)
     end
@@ -121,7 +127,7 @@
     it "should skip lists" do
       real_skip = @prot.method(:skip)
       @prot.should_receive(:read_list_begin).ordered.and_return([Types::DOUBLE, 11])
-      @prot.should_receive(:skip).with(Types::DOUBLE).ordered.exactly(11).times
+      @prot.should_receive(:read_double).ordered.exactly(11).times
       @prot.should_receive(:read_list_end)
       real_skip.call(Types::LIST)
     end
diff --git a/lib/rb/spec/serializer_spec.rb b/lib/rb/spec/serializer_spec.rb
index fe5a86a..78a11ab 100644
--- a/lib/rb/spec/serializer_spec.rb
+++ b/lib/rb/spec/serializer_spec.rb
@@ -8,15 +8,17 @@
 
   describe Serializer do
     it "should serialize structs to binary by default" do
-      serializer = Serializer.new
+      serializer = Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
       data = serializer.serialize(Hello.new(:greeting => "'Ello guv'nor!"))
       data.should == "\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00"
     end
 
     it "should serialize structs to the given protocol" do
-      protocol = mock("Protocol")
+      protocol = Protocol.new(mock("transport"))
       protocol.should_receive(:write_struct_begin).with("SpecNamespace::Hello")
-      protocol.should_receive(:write_field).with("greeting", Types::STRING, 1, "Good day")
+      protocol.should_receive(:write_field_begin).with("greeting", Types::STRING, 1)
+      protocol.should_receive(:write_string).with("Good day")
+      protocol.should_receive(:write_field_end)
       protocol.should_receive(:write_field_stop)
       protocol.should_receive(:write_struct_end)
       protocolFactory = mock("ProtocolFactory")
@@ -34,11 +36,11 @@
     end
 
     it "should deserialize structs from the given protocol" do
-      protocol = mock("Protocol")
+      protocol = Protocol.new(mock("transport"))
       protocol.should_receive(:read_struct_begin).and_return("SpecNamespace::Hello")
       protocol.should_receive(:read_field_begin).and_return(["greeting", Types::STRING, 1],
                                                             [nil, Types::STOP, 0])
-      protocol.should_receive(:read_type).with(Types::STRING).and_return("Good day")
+      protocol.should_receive(:read_string).and_return("Good day")
       protocol.should_receive(:read_field_end)
       protocol.should_receive(:read_struct_end)
       protocolFactory = mock("ProtocolFactory")
diff --git a/lib/rb/spec/spec_helper.rb b/lib/rb/spec/spec_helper.rb
index 53d2b80..a9e1374 100644
--- a/lib/rb/spec/spec_helper.rb
+++ b/lib/rb/spec/spec_helper.rb
@@ -31,3 +31,5 @@
     Thrift.type_checking = true
   end
 end
+
+require "thrift_native"
\ No newline at end of file
diff --git a/lib/rb/spec/struct_spec.rb b/lib/rb/spec/struct_spec.rb
index 4dc8714..1a22f57 100644
--- a/lib/rb/spec/struct_spec.rb
+++ b/lib/rb/spec/struct_spec.rb
@@ -1,6 +1,8 @@
 require File.dirname(__FILE__) + '/spec_helper'
 require File.dirname(__FILE__) + '/gen-rb/ThriftSpec_types'
 
+# require "binaryprotocolaccelerated"
+
 class ThriftStructSpec < Spec::ExampleGroup
   include Thrift
   include SpecNamespace
@@ -54,7 +56,7 @@
 
     it "should read itself off the wire" do
       struct = Foo.new
-      prot = mock("Protocol")
+      prot = Protocol.new(mock("transport"))
       prot.should_receive(:read_struct_begin).twice
       prot.should_receive(:read_struct_end).twice
       prot.should_receive(:read_field_begin).and_return(
@@ -79,14 +81,14 @@
       prot.should_receive(:read_list_end)
       prot.should_receive(:read_set_begin).and_return([Types::I16, 2])
       prot.should_receive(:read_set_end)
-      prot.should_receive(:read_type).with(Types::I32).and_return(
+      prot.should_receive(:read_i32).and_return(
         1, 14,        # complex keys
         42,           # simple
         4, 23, 4, 29  # ints
       )
-      prot.should_receive(:read_type).with(Types::STRING).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?")
-      prot.should_receive(:read_type).with(Types::DOUBLE).and_return(Math::PI, Math::E, 4.669201609)
-      prot.should_receive(:read_type).with(Types::I16).and_return(2, 3)
+      prot.should_receive(:read_string).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?")
+      prot.should_receive(:read_double).and_return(Math::PI, Math::E, 4.669201609)
+      prot.should_receive(:read_i16).and_return(2, 3)
       prot.should_not_receive(:skip)
       struct.read(prot)
 
@@ -100,7 +102,7 @@
 
     it "should skip unexpected fields in structs and use default values" do
       struct = Foo.new
-      prot = mock("Protocol")
+      prot = Protocol.new(mock("transport"))
       prot.should_receive(:read_struct_begin)
       prot.should_receive(:read_struct_end)
       prot.should_receive(:read_field_begin).and_return(
@@ -112,10 +114,12 @@
         [nil, Types::STOP, 0]
       )
       prot.should_receive(:read_field_end).exactly(5).times
-      prot.should_receive(:read_type).with(Types::I32).and_return(42)
-      prot.should_receive(:read_type).with(Types::STRING).and_return("foobar")
+      prot.should_receive(:read_i32).and_return(42)
+      prot.should_receive(:read_string).and_return("foobar")
       prot.should_receive(:skip).with(Types::STRUCT)
       prot.should_receive(:skip).with(Types::MAP)
+      # prot.should_receive(:read_map_begin).and_return([Types::I32, Types::I32, 0])
+      # prot.should_receive(:read_map_end)
       prot.should_receive(:skip).with(Types::I32)
       struct.read(prot)
 
@@ -128,31 +132,35 @@
     end
 
     it "should write itself to the wire" do
-      prot = mock("Protocol")
+      prot = Protocol.new(mock("transport")) #mock("Protocol")
       prot.should_receive(:write_struct_begin).with("SpecNamespace::Foo")
-      prot.should_receive(:write_struct_end)
+      prot.should_receive(:write_struct_begin).with("SpecNamespace::Hello")
+      prot.should_receive(:write_struct_end).twice
       prot.should_receive(:write_field_begin).with('ints', Types::LIST, 4)
+      prot.should_receive(:write_i32).with(1)
+      prot.should_receive(:write_i32).with(2).twice
+      prot.should_receive(:write_i32).with(3)
       prot.should_receive(:write_field_begin).with('complex', Types::MAP, 5)
+      prot.should_receive(:write_i32).with(5)
+      prot.should_receive(:write_string).with('foo')
+      prot.should_receive(:write_double).with(1.23)
       prot.should_receive(:write_field_begin).with('shorts', Types::SET, 6)
-      prot.should_receive(:write_field_stop)
-      prot.should_receive(:write_field_end).exactly(3).times
-      prot.should_receive(:write_field).with('simple', Types::I32, 1, 53)
-      prot.should_receive(:write_field).with('hello', Types::STRUCT, 3, Hello.new(:greeting => 'hello, world!'))
+      prot.should_receive(:write_i16).with(5)
+      prot.should_receive(:write_i16).with(17)
+      prot.should_receive(:write_i16).with(239)
+      prot.should_receive(:write_field_stop).twice
+      prot.should_receive(:write_field_end).exactly(6).times
+      prot.should_receive(:write_field_begin).with('simple', Types::I32, 1)
+      prot.should_receive(:write_i32).with(53)
+      prot.should_receive(:write_field_begin).with('hello', Types::STRUCT, 3)
+      prot.should_receive(:write_field_begin).with('greeting', Types::STRING, 1)
+      prot.should_receive(:write_string).with('hello, world!')
       prot.should_receive(:write_map_begin).with(Types::I32, Types::MAP, 1)
       prot.should_receive(:write_map_begin).with(Types::STRING, Types::DOUBLE, 1)
-      prot.should_receive(:write_type).with(Types::I32, 5) # complex/1/key
-      prot.should_receive(:write_type).with(Types::STRING, "foo") # complex/1/value/1/key
-      prot.should_receive(:write_type).with(Types::DOUBLE, 1.23) # complex/1/value/1/value
       prot.should_receive(:write_map_end).twice
       prot.should_receive(:write_list_begin).with(Types::I32, 4)
-      prot.should_receive(:write_type).with(Types::I32, 1)
-      prot.should_receive(:write_type).with(Types::I32, 2).twice
-      prot.should_receive(:write_type).with(Types::I32, 3)
       prot.should_receive(:write_list_end)
       prot.should_receive(:write_set_begin).with(Types::I16, 3)
-      prot.should_receive(:write_type).with(Types::I16, 5)
-      prot.should_receive(:write_type).with(Types::I16, 17)
-      prot.should_receive(:write_type).with(Types::I16, 239)
       prot.should_receive(:write_set_end)
 
       struct = Foo.new
@@ -199,12 +207,15 @@
         e.message.should == "something happened"
         e.code.should == 1
         # ensure it gets serialized properly, this is the really important part
-        prot = mock("Protocol")
+        prot = Protocol.new(mock("trans"))
         prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception")
         prot.should_receive(:write_struct_end)
-        prot.should_receive(:write_field).with('message', Types::STRING, 1, "something happened")
-        prot.should_receive(:write_field).with('code', Types::I32, 2, 1)
+        prot.should_receive(:write_field_begin).with('message', Types::STRING, 1)#, "something happened")
+        prot.should_receive(:write_string).with("something happened")
+        prot.should_receive(:write_field_begin).with('code', Types::I32, 2)#, 1)
+        prot.should_receive(:write_i32).with(1)
         prot.should_receive(:write_field_stop)
+        prot.should_receive(:write_field_end).twice
 
         e.write(prot)
       end
@@ -216,12 +227,15 @@
       rescue Thrift::Exception => e
         e.message.should == "something happened"
         e.code.should == 5
-        prot = mock("Protocol")
+        prot = Protocol.new(mock("trans"))
         prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception")
         prot.should_receive(:write_struct_end)
-        prot.should_receive(:write_field).with('message', Types::STRING, 1, "something happened")
-        prot.should_receive(:write_field).with('code', Types::I32, 2, 5)
+        prot.should_receive(:write_field_begin).with('message', Types::STRING, 1)
+        prot.should_receive(:write_string).with("something happened")
+        prot.should_receive(:write_field_begin).with('code', Types::I32, 2)
+        prot.should_receive(:write_i32).with(5)
         prot.should_receive(:write_field_stop)
+        prot.should_receive(:write_field_end).twice
 
         e.write(prot)
       end