You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by kc...@apache.org on 2008/07/08 02:47:50 UTC

svn commit: r674688 [1/2] - in /incubator/thrift/trunk: ./ lib/ lib/rb/ lib/rb/ext/ lib/rb/lib/thrift/ lib/rb/lib/thrift/protocol/ lib/rb/lib/thrift/server/ lib/rb/lib/thrift/transport/ lib/rb/spec/ test/ test/rb/ test/rb/benchmarks/ test/rb/core/ test...

Author: kclark
Date: Mon Jul  7 17:47:49 2008
New Revision: 674688

URL: http://svn.apache.org/viewvc?rev=674688&view=rev
Log:
Merge branch 'fastbinary'

Added:
    incubator/thrift/trunk/lib/rb/Makefile.am
    incubator/thrift/trunk/lib/rb/ext/
    incubator/thrift/trunk/lib/rb/ext/binaryprotocolaccelerated.c
    incubator/thrift/trunk/lib/rb/ext/extconf.rb
    incubator/thrift/trunk/lib/rb/lib/thrift/protocol/binaryprotocolaccelerated.rb
    incubator/thrift/trunk/lib/rb/setup.rb
    incubator/thrift/trunk/test/rb/Makefile.am
    incubator/thrift/trunk/test/rb/benchmarks/
    incubator/thrift/trunk/test/rb/benchmarks/protocol_benchmark.rb
    incubator/thrift/trunk/test/rb/core/test_binary_protocol_accelerated.rb
    incubator/thrift/trunk/test/rb/fixtures/
    incubator/thrift/trunk/test/rb/fixtures/structs.rb
    incubator/thrift/trunk/test/rb/integration/accelerated_buffered_client.rb
    incubator/thrift/trunk/test/rb/integration/accelerated_buffered_server.rb
    incubator/thrift/trunk/test/rb/integration/buffered_client.rb
    incubator/thrift/trunk/test/rb/integration/simple_client.rb
    incubator/thrift/trunk/test/rb/integration/simple_server.rb
Removed:
    incubator/thrift/trunk/test/rb/Makefile
Modified:
    incubator/thrift/trunk/configure.ac
    incubator/thrift/trunk/lib/Makefile.am
    incubator/thrift/trunk/lib/rb/Manifest
    incubator/thrift/trunk/lib/rb/Rakefile
    incubator/thrift/trunk/lib/rb/lib/thrift/server/nonblockingserver.rb
    incubator/thrift/trunk/lib/rb/lib/thrift/struct.rb
    incubator/thrift/trunk/lib/rb/lib/thrift/transport.rb
    incubator/thrift/trunk/lib/rb/lib/thrift/transport/socket.rb
    incubator/thrift/trunk/lib/rb/spec/socket_spec_shared.rb
    incubator/thrift/trunk/lib/rb/spec/transport_spec.rb
    incubator/thrift/trunk/test/Makefile.am
    incubator/thrift/trunk/test/SmallTest.thrift
    incubator/thrift/trunk/test/rb/generation/test_struct.rb
    incubator/thrift/trunk/test/rb/test_helper.rb
    incubator/thrift/trunk/test/rb/test_suite.rb

Modified: incubator/thrift/trunk/configure.ac
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/configure.ac?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/configure.ac (original)
+++ incubator/thrift/trunk/configure.ac Mon Jul  7 17:47:49 2008
@@ -61,6 +61,12 @@
 fi
 AM_CONDITIONAL(WITH_PYTHON, [test -n "$PYTHON" -a "$PYTHON" != ":"])
 
+AX_THRIFT_LIB(ruby, [Ruby], yes)
+if test "$with_ruby" = "yes"; then
+  AC_PATH_PROG([RUBY], [ruby])
+fi
+AM_CONDITIONAL(ENABLE_RUBY, [test -n "$RUBY"])
+
 AC_C_CONST
 AC_C_INLINE
 AC_C_VOLATILE
@@ -165,10 +171,12 @@
   lib/csharp/Makefile
   lib/java/Makefile
   lib/py/Makefile
+  lib/rb/Makefile
   if/Makefile
   test/Makefile
   test/py/Makefile
   test/java/Makefile
+  test/rb/Makefile
 ])
 
 AC_OUTPUT

Modified: incubator/thrift/trunk/lib/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/Makefile.am?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/Makefile.am (original)
+++ incubator/thrift/trunk/lib/Makefile.am Mon Jul  7 17:47:49 2008
@@ -17,12 +17,14 @@
 SUBDIRS += erl
 endif
 
+if ENABLE_RUBY
+SUBDIRS += rb
+endif
+
 EXTRA_DIST = \
   cocoa \
   hs    \
   ocaml \
   perl  \
   php   \
-  rb    \
-  erl   \
   st

Added: incubator/thrift/trunk/lib/rb/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/Makefile.am?rev=674688&view=auto
==============================================================================
--- incubator/thrift/trunk/lib/rb/Makefile.am (added)
+++ incubator/thrift/trunk/lib/rb/Makefile.am Mon Jul  7 17:47:49 2008
@@ -0,0 +1,13 @@
+EXTRA_DIST = setup.rb lib ext
+
+all-local:
+	$(RUBY) setup.rb config
+	$(RUBY) setup.rb setup
+
+install-exec-hook:
+	$(RUBY) setup.rb install
+
+clean-local:
+	$(RUBY) setup.rb clean
+
+check-local: all

Modified: incubator/thrift/trunk/lib/rb/Manifest
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/Manifest?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/rb/Manifest (original)
+++ incubator/thrift/trunk/lib/rb/Manifest Mon Jul  7 17:47:49 2008
@@ -8,11 +8,14 @@
 benchmark/thin_server.rb
 CHANGELOG
 COPYING
+ext/binaryprotocolaccelerated.c
+ext/extconf.rb
 lib/thrift/client.rb
 lib/thrift/deprecation.rb
 lib/thrift/exceptions.rb
 lib/thrift/processor.rb
 lib/thrift/protocol/binaryprotocol.rb
+lib/thrift/protocol/binaryprotocolaccelerated.rb
 lib/thrift/protocol/tbinaryprotocol.rb
 lib/thrift/protocol/tprotocol.rb
 lib/thrift/protocol.rb
@@ -34,9 +37,13 @@
 lib/thrift/types.rb
 lib/thrift.rb
 LICENSE
+Makefile
+Makefile.am
+Makefile.in
 Manifest
 Rakefile
 README
+setup.rb
 spec/backwards_compatibility_spec.rb
 spec/binaryprotocol_spec.rb
 spec/client_spec.rb

Modified: incubator/thrift/trunk/lib/rb/Rakefile
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/Rakefile?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/rb/Rakefile (original)
+++ incubator/thrift/trunk/lib/rb/Rakefile Mon Jul  7 17:47:49 2008
@@ -23,7 +23,7 @@
   # ensure this is a full thrift checkout and not a tarball of the ruby libs
   cmd = 'head -1 ../../README 2>/dev/null | grep Thrift >/dev/null 2>/dev/null'
   system(cmd) or fail "rake test requires a full thrift checkout"
-  sh 'make', '-C', File.dirname(__FILE__) + "/../../test/rb"
+  sh 'make', '-C', File.dirname(__FILE__) + "/../../test/rb", "check"
 end
 
 desc 'Compile the .thrift files for the specs'
@@ -57,6 +57,15 @@
     p.url = "http://incubator.apache.org/thrift/"
     p.include_rakefile = true
   end
+
+  task :install => [:check_site_lib]
+
+  require 'rbconfig'
+  task :check_site_lib do
+    if File.exist?(File.join(Config::CONFIG['sitelibdir'], 'thrift.rb'))
+      fail "thrift is already installed in site_ruby"
+    end
+  end
 rescue LoadError
   [:install, :package].each do |t|
     desc "Stub for #{t}"

Added: incubator/thrift/trunk/lib/rb/ext/binaryprotocolaccelerated.c
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/ext/binaryprotocolaccelerated.c?rev=674688&view=auto
==============================================================================
--- incubator/thrift/trunk/lib/rb/ext/binaryprotocolaccelerated.c (added)
+++ incubator/thrift/trunk/lib/rb/ext/binaryprotocolaccelerated.c Mon Jul  7 17:47:49 2008
@@ -0,0 +1,1231 @@
+// Half of this file comes from contributions from Nitay Joffe (nitay@powerset.com)
+// Much of the rest (almost) directly ported (or pulled) from thrift-py's fastbinary.c
+// Everything else via Kevin Clark (kevin@powerset.com)
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <ruby.h>
+#include <st.h>
+#include <netinet/in.h>
+
+// #define __DEBUG__
+
+#ifndef HAVE_STRLCPY
+
+static
+size_t
+strlcpy (char *dst, const char *src, size_t dst_sz)
+{
+    size_t n;
+
+    for (n = 0; n < dst_sz; n++) {
+      if ((*dst++ = *src++) == '\0')
+        break;
+    }
+
+    if (n < dst_sz)
+      return n;
+    if (n > 0)
+      *(dst - 1) = '\0';
+    return n + strlen (src);
+}
+
+#endif
+
+#define dbg() fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__)
+
+
+// TODO (kevinclark): This was here from the patch/python. Not sure
+// If it's actually that big a pain. Need to look into pulling
+// From the right place
+
+// Stolen out of TProtocol.h.
+// It would be a huge pain to have both get this from one place.
+
+enum TType {
+  T_STOP       = 0,
+  T_BOOL       = 2,
+  T_BYTE       = 3,
+  T_I16        = 6,
+  T_I32        = 8,
+  T_I64        = 10,
+  T_DBL        = 4,
+  T_STR        = 11,
+  T_STRCT      = 12,
+  T_MAP        = 13,
+  T_SET        = 14,
+  T_LIST       = 15
+  // T_VOID       = 1,
+  // T_I08        = 3,
+  // T_U64        = 9,
+  // T_UTF7       = 11,
+  // T_UTF8       = 16,
+  // T_UTF16      = 17
+};
+
+#define IS_CONTAINER(x) (x == T_MAP || x == T_SET || x == T_LIST)
+
+// Same comment as the enum.  Sorry.
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+
+#ifndef __BYTE_ORDER
+# if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+#  define __BYTE_ORDER BYTE_ORDER
+#  define __LITTLE_ENDIAN LITTLE_ENDIAN
+#  define __BIG_ENDIAN BIG_ENDIAN
+# else
+#  error "Cannot determine endianness"
+# endif
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+# define ntohll(n) (n)
+# define htonll(n) (n)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+# if defined(__GNUC__) && defined(__GLIBC__)
+#  include <byteswap.h>
+#  define ntohll(n) bswap_64(n)
+#  define htonll(n) bswap_64(n)
+# else /* GNUC & GLIBC */
+#  define ntohll(n) ( (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32) )
+#  define htonll(n) ( (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32) )
+# endif /* GNUC & GLIBC */
+#else /* __BYTE_ORDER */
+# error "Can't define htonll or ntohll!"
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Cached interned strings and such
+// -----------------------------------------------------------------------------
+
+static VALUE class_tbpa;
+static VALUE m_thrift;
+static VALUE rb_cSet;
+static ID type_sym;
+static ID class_sym;
+static ID key_sym;
+static ID value_sym;
+static ID element_sym;
+static ID name_sym;
+static ID fields_id;
+static ID consume_bang_id;
+static ID string_buffer_id;
+static ID borrow_id;
+static ID keys_id;
+static ID entries_id;
+
+static const uint32_t VERSION_MASK = 0xffff0000;
+static const uint32_t VERSION_1 = 0x80010000;
+
+// -----------------------------------------------------------------------------
+// Structs so I don't have to keep calling rb_hash_aref
+// -----------------------------------------------------------------------------
+
+// { :type => field[:type],
+//   :class => field[:class],
+//   :key => field[:key],
+//   :value => field[:value],
+//   :element => field[:element] }
+
+struct _thrift_map;
+struct _field_spec;
+
+typedef union {
+  VALUE class;
+  struct _thrift_map* map;
+  struct _field_spec* element;
+} container_data;
+
+typedef struct _field_spec {
+  int type;
+  char* name;
+  container_data data;
+} field_spec;
+
+typedef struct _thrift_map {
+  field_spec* key;
+  field_spec* value; 
+} thrift_map;
+
+
+static void free_field_spec(field_spec* spec) {
+  switch(spec->type) {
+    case T_LIST:
+    case T_SET:
+      free_field_spec(spec->data.element);
+      break;
+    
+    case T_MAP:
+      free_field_spec(spec->data.map->key);
+      free_field_spec(spec->data.map->value);
+      free(spec->data.map);
+      break;
+  }
+  
+  free(spec);
+}
+
+// Parses a ruby field spec into a C struct
+//
+// Simple fields look like:
+// { :name => .., :type => .. }
+// Structs add the :class attribute
+// Maps adds :key and :value attributes, field specs
+// Lists and Sets add an :element, a field spec
+static field_spec* parse_field_spec(VALUE field_data) {
+  int type = NUM2INT(rb_hash_aref(field_data, type_sym));
+  VALUE name = rb_hash_aref(field_data, name_sym);
+  field_spec* spec = (field_spec *) malloc(sizeof(field_spec));
+
+#ifdef __DEBUG__ // No need for this in prod since I set all the fields
+  bzero(spec, sizeof(field_spec));
+#endif
+
+  spec->type = type;
+  
+  if (Qnil != name) {
+    spec->name = StringValuePtr(name);
+  } else {
+    spec->name = NULL;
+  }
+  
+  switch(type) {
+    case T_STRCT: {
+      spec->data.class = rb_hash_aref(field_data, class_sym);
+      break;
+    }
+    
+    case T_MAP: {
+      VALUE key_fields = rb_hash_aref(field_data, key_sym);
+      VALUE value_fields = rb_hash_aref(field_data, value_sym);
+      thrift_map* map = (thrift_map *) malloc(sizeof(thrift_map));
+      
+      map->key = parse_field_spec(key_fields);
+      map->value = parse_field_spec(value_fields);
+      spec->data.map = map;
+      
+      break;
+    }
+    
+    case T_LIST: 
+    case T_SET:
+    {
+      VALUE list_fields = rb_hash_aref(field_data, element_sym);
+      spec->data.element = parse_field_spec(list_fields);
+      break;
+    }
+  }
+  
+  return spec;
+}
+
+
+// -----------------------------------------------------------------------------
+// Serialization routines
+// -----------------------------------------------------------------------------
+
+
+// write_*(VALUE buf, ...) takes a value and adds it to a Ruby string buffer,
+// in network order
+static void write_byte(VALUE buf, int8_t val) {
+  rb_str_buf_cat(buf, (char*)&val, sizeof(int8_t));
+}
+
+static void write_i16(VALUE buf, int16_t val) {
+  int16_t net = (int16_t)htons(val);
+  rb_str_buf_cat(buf, (char*)&net, sizeof(int16_t));
+}
+
+static void write_i32(VALUE buf, int32_t val) {
+  int32_t net = (int32_t)htonl(val);
+  rb_str_buf_cat(buf, (char*)&net, sizeof(int32_t));
+}
+
+static void write_i64(VALUE buf, int64_t val) {
+  int64_t net = (int64_t)htonll(val);
+  rb_str_buf_cat(buf, (char*)&net, sizeof(int64_t));
+}
+
+static void write_double(VALUE buf, double dub) {
+  // Unfortunately, bitwise_cast doesn't work in C.  Bad C!
+  union {
+    double f;
+    int64_t t;
+  } transfer;
+  transfer.f = dub;
+  write_i64(buf, transfer.t);
+}
+
+static void write_string(VALUE buf, char* str) {
+  int32_t len = strlen(str);
+  write_i32(buf, len);
+  rb_str_buf_cat2(buf, str);
+}
+
+// Some functions macro'd out because they're nops for the binary protocol
+// but placeholders were desired in case things change
+#define write_struct_begin(buf)
+#define write_struct_end(buf)
+
+static void write_field_begin(VALUE buf, char* name, int type, int fid) {
+#ifdef __DEBUG__
+  fprintf(stderr, "Writing field beginning: %s %d %d\n", name, type, fid);
+#endif
+
+  write_byte(buf, (int8_t)type);
+  write_i16(buf, (int16_t)fid);
+}
+
+#define write_field_end(buf)
+
+static void write_field_stop(VALUE buf) {
+  write_byte(buf, T_STOP);
+}
+
+static void write_map_begin(VALUE buf, int8_t ktype, int8_t vtype, int32_t sz) {
+  write_byte(buf, ktype);
+  write_byte(buf, vtype);
+  write_i32(buf, sz);
+}
+
+#define write_map_end(buf);
+
+static void write_list_begin(VALUE buf, int type, int sz) {
+  write_byte(buf, type);
+  write_i32(buf, sz);
+}
+
+#define write_list_end(buf)
+
+static void write_set_begin(VALUE buf, int type, int sz) {
+  write_byte(buf, type);
+  write_i32(buf, sz);
+}
+
+#define write_set_end(buf)
+
+static void binary_encoding(VALUE buf, VALUE obj, int type);
+
+// Handles container types: Map, Set, List
+static void write_container(VALUE buf, VALUE value, field_spec* spec) {
+  int sz, i;
+  
+  switch(spec->type) {
+    case T_MAP: {
+      VALUE keys;
+      VALUE key;
+      VALUE val;
+      
+      keys = rb_funcall(value, keys_id, 0);
+      
+      sz = RARRAY(keys)->len;
+      
+      write_map_begin(buf, spec->data.map->key->type, spec->data.map->value->type, sz);
+      
+      for (i = 0; i < sz; i++) {
+        key = rb_ary_entry(keys, i);
+        val = rb_hash_aref(value, key);
+        
+        if (IS_CONTAINER(spec->data.map->key->type)) {
+          write_container(buf, key, spec->data.map->key);
+        } else {
+          binary_encoding(buf, key, spec->data.map->key->type);
+        }
+        
+        if (IS_CONTAINER(spec->data.map->value->type)) {
+          write_container(buf, val, spec->data.map->value);
+        } else {
+          binary_encoding(buf, val, spec->data.map->value->type);
+        }
+      }
+      
+      write_map_end(buf);
+
+      break;
+    }
+    
+    case T_LIST: {
+      sz = RARRAY(value)->len;
+      
+      write_list_begin(buf, spec->data.element->type, sz);
+      for (i = 0; i < sz; ++i) {
+        if (IS_CONTAINER(spec->data.element->type)) {
+          write_container(buf, rb_ary_entry(value, i), spec->data.element);
+        } else {
+          binary_encoding(buf, rb_ary_entry(value, i), spec->data.element->type);
+        }
+      }
+      write_list_end(buf);
+      break;
+    }
+
+    case T_SET: {
+      VALUE items;
+
+      if (TYPE(value) == T_ARRAY) {
+        items = value;
+      } else {        
+        if (rb_cSet == CLASS_OF(value)) {
+          items = rb_funcall(value, entries_id, 0);
+        } else {
+          Check_Type(value, T_HASH);
+          items = rb_funcall(value, keys_id, 0);
+        }
+      }
+
+      sz = RARRAY(items)->len;
+      
+      write_set_begin(buf, spec->data.element->type, sz);
+      
+      for (i = 0; i < sz; i++) {
+        if (IS_CONTAINER(spec->data.element->type)) {
+          write_container(buf, rb_ary_entry(items, i), spec->data.element);
+        } else {
+          binary_encoding(buf, rb_ary_entry(items, i), spec->data.element->type);
+        }
+      }
+      
+      write_set_end(buf);
+      break;
+    }
+  }
+}
+
+// Takes the field id, data to be encoded, buffer and enclosing object
+// to be encoded. buf and obj passed as a ruby array for rb_hash_foreach.
+// TODO(kevinclark): See if they can be passed individually to avoid object
+// creation
+static int encode_field(VALUE fid, VALUE data, VALUE ary) {
+  field_spec *spec = parse_field_spec(data);
+  
+  VALUE buf = rb_ary_entry(ary, 0);
+  VALUE obj = rb_ary_entry(ary, 1);
+  char name_buf[128];
+  
+  name_buf[0] = '@';
+  strlcpy(&name_buf[1], spec->name, sizeof(name_buf) - 1);
+  
+  VALUE value = rb_ivar_get(obj, rb_intern(name_buf));
+  
+  if (Qnil == value) {
+    free_field_spec(spec);
+    return 0;
+  }
+     
+  write_field_begin(buf, spec->name, spec->type, NUM2INT(fid));
+  
+  if (IS_CONTAINER(spec->type)) {
+    write_container(buf, value, spec);
+  } else {
+    binary_encoding(buf, value, spec->type);
+  }
+  write_field_end(buf);
+  
+  free_field_spec(spec);
+  
+  return 0;
+}
+
+// -----------------------------------------------------------------------------
+// TFastBinaryProtocol's main encoding loop
+// -----------------------------------------------------------------------------
+
+static void binary_encoding(VALUE buf, VALUE obj, int type) {
+#ifdef __DEBUG__
+  rb_p(rb_str_new2("Encoding binary (buf, obj, type)"));
+  rb_p(rb_inspect(buf));
+  rb_p(rb_inspect(obj));
+  rb_p(rb_inspect(INT2FIX(type)));
+#endif
+
+  switch(type) {
+    case T_BOOL:
+      if RTEST(obj) {
+        write_byte(buf, 1);
+      }
+      else {
+        write_byte(buf, 0);
+      }
+      
+      break;
+    
+    case T_BYTE:
+      write_byte(buf, NUM2INT(obj));
+      break;
+    
+    case T_I16:
+      write_i16(buf, NUM2INT(obj));
+      break;
+    
+    case T_I32:
+      write_i32(buf, NUM2INT(obj));
+      break;
+    
+    case T_I64:
+      write_i64(buf, rb_num2ll(obj));
+      break;
+    
+    case T_DBL:
+      write_double(buf, NUM2DBL(obj));
+      break;
+
+    case T_STR:
+      write_string(buf, StringValuePtr(obj));
+      break;
+          
+    case T_STRCT: {
+      // rb_hash_foreach has to take args as a ruby array
+      VALUE args = rb_ary_new3(2, buf, obj);
+      VALUE fields = rb_const_get(CLASS_OF(obj), fields_id);
+      
+      write_struct_begin(buf);
+      
+      rb_hash_foreach(fields, encode_field, args);
+      
+      write_field_stop(buf);
+      write_struct_end(buf);
+      break;
+    }
+    
+    default: {
+      rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", type);
+    }
+  }
+}
+
+// obj is always going to be a TSTRCT
+static VALUE tbpa_encode_binary(VALUE self, VALUE obj) {
+  VALUE buf = rb_str_buf_new(1024);
+  binary_encoding(buf, obj, T_STRCT);
+  return buf;
+}
+
+
+// -----------------------------------------------------------------------------
+// Read stuff
+// -----------------------------------------------------------------------------
+
+typedef struct {
+  char* name;
+  int8_t type;
+  int16_t id;
+} field_header;
+
+typedef struct {
+  int8_t key_type;
+  int8_t val_type;
+  int num_entries;
+} map_header;
+
+typedef struct {
+  int8_t type;
+  int num_elements;
+} list_header;
+
+typedef list_header set_header;
+
+typedef struct {
+  char* data;
+  int pos;
+  int len;
+  VALUE trans;
+} decode_buffer;
+
+typedef struct {
+  char* ptr;
+  int len;
+} thrift_string;
+
+#define read_struct_begin(buf)
+#define read_struct_end(buf)
+
+// This prototype is required to be able to run a call through rb_protect
+// which rescues from ruby exceptions
+static VALUE protectable_consume(VALUE args) {
+  VALUE trans = rb_ary_entry(args, 0);
+  VALUE size = rb_ary_entry(args, 1);
+  
+  return rb_funcall(trans, consume_bang_id, 1, size);
+}
+
+// Clears size bytes from the transport's string buffer
+static bool consume(decode_buffer* buf, int32_t size) {
+  if (size != 0) {
+    VALUE ret;
+    VALUE args = rb_ary_new3(2, buf->trans, INT2FIX(size));
+    int status = 0;
+    
+    ret = rb_protect(protectable_consume, args, &status);
+    
+    if (status) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+  
+  // Nothing to consume, we're all good
+  return true;
+}
+
+// This prototype is required to be able to run a call through rb_protect
+// which rescues from ruby exceptions
+static VALUE protectable_borrow(VALUE args) {
+  VALUE trans = rb_ary_entry(args, 0);
+  
+  switch(RARRAY(args)->len) {
+    case 1:
+      return rb_funcall(trans, borrow_id, 0);
+    
+    case 2: {
+      VALUE size = rb_ary_entry(args, 1);
+      return rb_funcall(trans, borrow_id, 1, size);
+    }
+  }
+  
+  return Qnil;
+}
+
+// Calls into the transport to get the available string buffer
+static bool borrow(decode_buffer* buf, int32_t size, VALUE* dst) {
+  int status = 0;
+  VALUE args;
+  
+  if (size == 0) {
+    args = rb_ary_new3(1, buf->trans);
+  } else {
+    args = rb_ary_new3(2, buf->trans, INT2FIX(size));
+  }
+
+  *dst = rb_protect(protectable_borrow, args, &status);
+  
+  return (status == 0);
+}
+
+// Refills the buffer by calling borrow. If buf->pos is nonzero that number of bytes
+// is cleared through consume.
+//
+// returns: 0 on success, non-zero on failure. On error buf is unchanged.
+static int fill_buffer(decode_buffer* buf, int32_t req_len) {
+  VALUE refill;
+  
+  if (!consume(buf, buf->pos)) {
+    return -1;
+  }
+
+  if (!borrow(buf, req_len, &refill)) {
+    return -2;
+  }
+  
+  buf->data = StringValuePtr(refill);
+  buf->len = RSTRING(refill)->len;
+  buf->pos = 0;
+  
+  return 0;
+}
+
+
+// read_bytes pulls a number of bytes (size) from the buffer, refilling if needed,
+// and places them in dst. This should _always_ be used used when reading from the buffer
+// or buffered transports will be upset with you.
+static bool read_bytes(decode_buffer* buf, void* dst, size_t size) {
+  int avail = (buf->len - buf->pos);
+  
+  if (size <= avail) {
+    memcpy(dst, buf->data + buf->pos, size);
+    buf->pos += size;
+  } else {
+    
+    if (avail > 0) {
+      // Copy what we can
+      memcpy(dst, buf->data + buf->pos, avail);
+      buf->pos += avail;
+    }
+
+    if (fill_buffer(buf, size - avail) < 0) {
+      return false;
+    }
+    
+    memcpy(dst + avail, buf->data, size - avail);
+    buf->pos += size - avail;
+  }
+  
+  return true;
+}
+
+// -----------------------------------------------------------------------------
+// Helpers for grabbing specific types from the buffer
+// -----------------------------------------------------------------------------
+
+static bool read_byte(decode_buffer* buf, int8_t* data) {
+  return read_bytes(buf, data, sizeof(int8_t));
+}
+
+static bool read_int16(decode_buffer* buf, int16_t* data) {
+  bool success = read_bytes(buf, data, sizeof(int16_t));
+  *data = ntohs(*data);
+  
+  return success;
+}
+
+static bool read_int32(decode_buffer* buf, int32_t* data) {
+  bool success = read_bytes(buf, data, sizeof(int32_t));
+  *data = ntohl(*data);
+  
+  return success;
+}
+
+static bool read_int64(decode_buffer* buf, int64_t* data) {
+  bool success = read_bytes(buf, data, sizeof(int64_t));
+  *data = ntohll(*data);
+  
+  return success;
+}
+
+static bool read_double(decode_buffer* buf, double* data) {
+  return read_int64(buf, (int64_t*)data);
+}
+
+static bool read_string(decode_buffer* buf, VALUE* data) {
+  int len;
+  
+  if (!read_int32(buf, &len)) {
+    return false;
+  }
+  
+  if (buf->len - buf->pos >= len) {
+    *data = rb_str_new(buf->data + buf->pos, len);
+    buf->pos += len;
+  } 
+  else {
+    char* str;
+    
+    if ((str = (char*) malloc(len)) == NULL) {
+      return false;
+    }
+    
+    if (!read_bytes(buf, str, len)) {
+      free(str);
+      return false;
+    }
+    
+    *data = rb_str_new(str, len);
+    
+    free(str);
+  }
+  
+  return true;
+}
+
+static bool read_field_begin(decode_buffer* buf, field_header* header) {
+#ifdef __DEBUG__ // No need for this in prod since I set all the fields
+  bzero(header, sizeof(field_header));
+#endif
+
+  header->name = NULL;
+  
+  if (!read_byte(buf, &header->type)) {
+    return false;
+  }
+  
+  if (header->type == T_STOP) {
+    header->id = 0;
+  } else {
+    if (!read_int16(buf, &header->id)) {
+      return false;
+    }
+  }
+  
+  return true;
+}
+
+#define read_field_end(buf)
+
+static bool read_map_begin(decode_buffer* buf, map_header* header) {
+#ifdef __DEBUG__ // No need for this in prod since I set all the fields
+  bzero(header, sizeof(map_header));
+#endif
+  
+  return (read_byte(buf, &header->key_type) && 
+          read_byte(buf, &header->val_type) &&
+          read_int32(buf, &header->num_entries));
+}
+
+#define read_map_end(buf)
+
+static bool read_list_begin(decode_buffer* buf, list_header* header) {
+#ifdef __DEBUG__ // No need for this in prod since I set all the fields
+  bzero(header, sizeof(list_header));
+#endif
+  
+  if (!read_byte(buf, &header->type) || !read_int32(buf, &header->num_elements)) {
+    return false;
+  } else {
+    return true;
+  }
+}
+
+#define read_list_end(buf)
+
+#define read_set_begin read_list_begin
+#define read_set_end read_list_end
+
+
+// High level reader function with ruby type coercion
+static bool read_type(int type, decode_buffer* buf, VALUE* dst) {
+  switch(type) {
+    case T_BOOL: {
+      int8_t byte;
+      
+      if (!read_byte(buf, &byte)) {
+        return false;
+      }
+      
+      if (0 == byte) {
+        *dst = Qfalse;
+      } else {
+        *dst = Qtrue;
+      }
+      
+      break;
+    }
+    
+    case T_BYTE: {
+      int8_t byte;
+
+      if (!read_byte(buf, &byte)) {
+        return false;
+      }
+
+      *dst = INT2FIX(byte);
+      break;
+    }
+    
+    case T_I16: {
+      int16_t i16;
+      
+      if (!read_int16(buf, &i16)) {
+        return false;
+      }
+      
+      *dst = INT2FIX(i16);
+      break;
+    }
+
+    case T_I32: {
+      int32_t i32;
+      
+      if (!read_int32(buf, &i32)) {
+        return false;
+      }
+      
+      *dst = INT2NUM(i32);
+      break;
+    }
+
+    case T_I64: {
+      int64_t i64;
+      
+      if (!read_int64(buf, &i64)) {
+        return false;
+      }
+      
+      *dst = rb_ll2inum(i64);
+      break;
+    }
+    
+    case T_DBL: {
+      double dbl;
+      
+      if (!read_double(buf, &dbl)) {
+        return false;
+      }
+      
+      *dst = rb_float_new(dbl);
+      break;
+    }
+
+    case T_STR: {
+      VALUE str;
+      
+      if (!read_string(buf, &str)) {
+        return false;
+      }
+      
+      *dst = str;
+      break;
+    }
+  }
+  
+  return true;
+}
+
+// TODO(kevinclark): Now that read_string does a malloc,
+// This maybe could be modified to avoid that, and the type coercion
+
+// Read the bytes but don't do anything with the value
+static bool skip_type(int type, decode_buffer* buf) {
+  switch (type) {
+    case T_STRCT:
+      read_struct_begin(buf);
+      while (true) {
+        field_header header;
+
+        if (!read_field_begin(buf, &header)) {
+          return false;
+        }
+
+        if (header.type == T_STOP) {
+          break;
+        }
+
+        if (!skip_type(header.type, buf)) {
+          return false;
+        }
+
+        read_field_end(buf);
+      }
+      read_struct_end(buf);
+
+      break;
+
+    case T_MAP: {
+      int i;
+      map_header header;
+
+      if (!read_map_begin(buf, &header)) {
+        return false;
+      }
+
+      for (i = 0; i < header.num_entries; ++i) {
+        if (!skip_type(header.key_type, buf)) {
+          return false;
+        }
+        if (!skip_type(header.val_type, buf)) {
+          return false;
+        }
+      }
+
+      read_map_end(buf);
+      break;
+    }
+
+    case T_SET: {
+      int i;
+      set_header header;
+
+      if (!read_set_begin(buf, &header)) {
+        return false;
+      }
+
+      for (i = 0; i < header.num_elements; ++i) {
+        if (!skip_type(header.type, buf)) {
+          return false;
+        }
+      }
+
+      read_set_end(buf);
+      break;
+    }
+
+    case T_LIST: {
+      int i;
+      list_header header;
+
+      if (!read_list_begin(buf, &header)) {
+        return false;
+      }
+
+      for (i = 0; i < header.num_elements; ++i) {
+        if (!skip_type(header.type, buf)) {
+          return false;
+        }
+      }
+
+      read_list_end(buf);
+      break;
+    }
+
+    default: {
+      VALUE v;
+      if (!read_type(type, buf, &v)) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+
+static VALUE read_struct(VALUE obj, decode_buffer* buf);
+
+// Read the right thing from the buffer given the field spec
+// and return the ruby object
+static bool read_field(decode_buffer* buf, field_spec* spec, VALUE* dst) {
+  switch (spec->type) {
+    case T_STRCT: {
+      VALUE obj = rb_class_new_instance(0, NULL, spec->data.class);
+      
+      *dst = read_struct(obj, buf);
+      break;
+    }
+    
+    case T_MAP: {
+      map_header hdr;
+      VALUE hsh;
+      int i;
+      
+      read_map_begin(buf, &hdr); 
+      hsh = rb_hash_new();
+      
+      for (i = 0; i < hdr.num_entries; ++i) {
+        VALUE key, val;
+        
+        if (!read_field(buf, spec->data.map->key, &key)) {
+          return false;
+        }
+        
+        if (!read_field(buf, spec->data.map->value, &val)) {
+          return false;
+        }
+        
+        rb_hash_aset(hsh, key, val);
+      }
+      
+      read_map_end(buf);
+      
+      *dst = hsh;
+      break;
+    }
+    
+    case T_LIST: {
+      list_header hdr;
+      VALUE arr, element;
+      int i;
+      
+      read_list_begin(buf, &hdr);
+      arr = rb_ary_new2(hdr.num_elements);
+      
+      for (i = 0; i < hdr.num_elements; ++i) {
+        if (!read_field(buf, spec->data.element, &element)) {
+          return false;
+        }
+
+        rb_ary_push(arr, element);
+      }
+      
+      read_list_end(buf);
+      
+      *dst = arr;
+      break;
+    }
+    
+    case T_SET: {
+      VALUE items, item;
+      set_header hdr;
+      int i;
+      
+      read_set_begin(buf, &hdr);
+      items = rb_ary_new2(hdr.num_elements);
+      
+      for (i = 0; i < hdr.num_elements; ++i) {
+        if (!read_field(buf, spec->data.element, &item)) {
+          return false;
+        }
+        
+        rb_ary_push(items, item);
+      }
+      
+      *dst = rb_class_new_instance(1, &items, rb_cSet);
+      break;
+    }
+    
+    
+    default:
+      return read_type(spec->type, buf, dst);
+  }
+  
+  return true;
+}
+
+static void handle_read_error() {
+  // If it was an exception, reraise
+  if (!NIL_P(ruby_errinfo)) {
+    rb_exc_raise(ruby_errinfo);
+  } else {
+    // Something else went wrong, no idea what would call this yet
+    // So far, the only thing to cause failures underneath is ruby
+    // exceptions. Follow up on this regularly -- Kevin Clark (TODO)
+    rb_raise(rb_eStandardError, "[BUG] Something went wrong in the field reading, but not a ruby exception");
+  }
+}
+
+// Fill in the instance variables in an object (thrift struct)
+// from the decode buffer
+static VALUE read_struct(VALUE obj, decode_buffer* buf) {
+  VALUE field;
+  field_header f_header;
+  VALUE value = Qnil;
+  VALUE fields = rb_const_get(CLASS_OF(obj), fields_id);
+  field_spec* spec;
+  char name_buf[128];
+    
+  read_struct_begin(buf);
+  
+  while(true) {
+    if (!read_field_begin(buf, &f_header)) {
+      handle_read_error();
+    }
+    
+    if (T_STOP == f_header.type) {
+      break;
+    }
+    
+    field = rb_hash_aref(fields, INT2FIX(f_header.id));
+    
+    if (NIL_P(field)) {
+      if (!skip_type(f_header.type, buf)) {
+        handle_read_error();
+        return Qnil;
+      }
+    } 
+    else {
+      spec = parse_field_spec(field);
+
+      if (spec->type != f_header.type) {
+        if (!skip_type(spec->type, buf)) {
+          free_field_spec(spec);
+          handle_read_error();
+          return Qnil;
+        }
+      } else {
+        // Read busted somewhere (probably borrow/consume), bail
+        if (!read_field(buf, spec, &value)) {
+          free_field_spec(spec);
+          handle_read_error();
+          return Qnil;
+        }
+        
+        name_buf[0] = '@';
+        strlcpy(&name_buf[1], spec->name, sizeof(name_buf) - 1);
+        
+        rb_iv_set(obj, name_buf, value);
+      }
+      
+      free_field_spec(spec);
+    }
+    
+    read_field_end(buf);
+  }
+  
+  read_struct_end(buf);
+  
+  return obj;
+}
+
+
+// Takes an object and transport, and decodes the values in the transport's
+// buffer to fill the object.
+static VALUE tbpa_decode_binary(VALUE self, VALUE obj, VALUE transport) {
+  decode_buffer buf;
+  VALUE ret_val;
+
+  buf.pos = 0;    // This needs to be set so an arbitrary number of bytes isn't consumed
+  buf.trans = transport;       // We need to hold this so the buffer can be refilled
+  
+  if (fill_buffer(&buf, 0) < 0) {
+    handle_read_error();
+    return Qnil;
+  }
+
+#ifdef __DEBUG__
+  rb_p(rb_str_new2("Running decode binary with data:"));
+  rb_p(rb_inspect(rb_str_new2(buf.data)));
+#endif
+ 
+  ret_val = read_struct(obj, &buf);
+  
+  // Consume whatever was read
+  consume(&buf, buf.pos);
+  
+  return ret_val;
+}
+
+// -----------------------------------------------------------------------------
+// These methods are used by the thrift library and need to handled
+// seperately from encode and decode
+// -----------------------------------------------------------------------------
+
+// Read the message header and return it as a ruby array
+static VALUE tbpa_read_message_begin(VALUE self) {
+  decode_buffer buf;
+  int32_t version, seqid;
+  int8_t type;
+  VALUE name;
+  
+  VALUE trans = rb_iv_get(self, "@trans");
+  
+  buf.pos = 0;              // This needs to be set so fill_buffer doesn't consume
+  buf.trans = trans;        // We need to hold this so the buffer can be refilled
+  
+
+  if (fill_buffer(&buf, 0) < 0 || !read_int32(&buf, &version)) {
+    // Consume whatever was read
+    consume(&buf, buf.pos);
+    handle_read_error();
+    return Qnil;
+  }
+  
+  if ((version & VERSION_MASK) != VERSION_1) {
+    VALUE tprotocol_exception = rb_const_get(rb_cObject, rb_intern("TProtocolException"));
+    VALUE exception = rb_funcall(tprotocol_exception, rb_intern("new"), 2, rb_const_get(tprotocol_exception, rb_intern("BAD_VERSION")), rb_str_new2("Missing version identifier"));
+    rb_raise(exception, "");
+  }
+  
+  type = version & 0x000000ff;
+  
+  if (!read_string(&buf, &name) || !read_int32(&buf, &seqid)) {
+    // Consume whatever was read
+    consume(&buf, buf.pos);
+    handle_read_error();
+    return Qnil;
+  }
+  
+  // Consume whatever was read
+  if (consume(&buf, buf.pos) < 0) {
+    handle_read_error();
+    return Qnil;
+  }
+  
+  return rb_ary_new3(3, name, INT2FIX(type), INT2FIX(seqid));
+}
+
+void Init_binaryprotocolaccelerated()
+{
+  m_thrift = rb_const_get(rb_cObject, rb_intern("Thrift"));
+  VALUE class_tbinproto = rb_const_get(m_thrift, rb_intern("BinaryProtocol"));
+  class_tbpa = rb_define_class_under(m_thrift, "BinaryProtocolAccelerated", class_tbinproto);
+  type_sym = ID2SYM(rb_intern("type"));
+  class_sym = ID2SYM(rb_intern("class"));
+  key_sym = ID2SYM(rb_intern("key"));
+  value_sym = ID2SYM(rb_intern("value"));
+  name_sym = ID2SYM(rb_intern("name"));
+  fields_id = rb_intern("FIELDS");
+  element_sym = ID2SYM(rb_intern("element"));
+  consume_bang_id = rb_intern("consume!");
+  string_buffer_id = rb_intern("string_buffer");
+  borrow_id = rb_intern("borrow");
+  keys_id = rb_intern("keys");
+  entries_id = rb_intern("entries");
+  rb_cSet = rb_const_get(rb_cObject, rb_intern("Set"));
+  
+  // For fast access
+  rb_define_method(class_tbpa, "encode_binary", tbpa_encode_binary, 1);
+  rb_define_method(class_tbpa, "decode_binary", tbpa_decode_binary, 2);
+  rb_define_method(class_tbpa, "read_message_begin", tbpa_read_message_begin, 0);
+  
+}

Added: incubator/thrift/trunk/lib/rb/ext/extconf.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/ext/extconf.rb?rev=674688&view=auto
==============================================================================
--- incubator/thrift/trunk/lib/rb/ext/extconf.rb (added)
+++ incubator/thrift/trunk/lib/rb/ext/extconf.rb Mon Jul  7 17:47:49 2008
@@ -0,0 +1,7 @@
+require 'mkmf'
+
+$CFLAGS = "-g -O2 -Wall -Werror"
+
+have_func("strlcpy", "string.h")
+
+create_makefile 'binaryprotocolaccelerated'

Added: incubator/thrift/trunk/lib/rb/lib/thrift/protocol/binaryprotocolaccelerated.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/lib/thrift/protocol/binaryprotocolaccelerated.rb?rev=674688&view=auto
==============================================================================
--- incubator/thrift/trunk/lib/rb/lib/thrift/protocol/binaryprotocolaccelerated.rb (added)
+++ incubator/thrift/trunk/lib/rb/lib/thrift/protocol/binaryprotocolaccelerated.rb Mon Jul  7 17:47:49 2008
@@ -0,0 +1,19 @@
+require 'thrift/protocol/binaryprotocol'
+require 'binaryprotocolaccelerated'
+
+=begin
+The only change required for a transport to support TBinaryProtocolAccelerated is to implement 2 methods:
+  * borrow(size), which takes an optional argument and returns atleast _size_ bytes from the transport, 
+                  or the default buffer size if no argument is given
+  * consume!(size), which removes size bytes from the front of the buffer
+
+See TMemoryBuffer and TBufferedTransport for examples.
+=end
+
+module Thrift
+  class BinaryProtocolAcceleratedFactory < ProtocolFactory
+    def get_protocol(trans)
+      BinaryProtocolAccelerated.new(trans)
+    end
+  end
+end

Modified: incubator/thrift/trunk/lib/rb/lib/thrift/server/nonblockingserver.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/lib/thrift/server/nonblockingserver.rb?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/rb/lib/thrift/server/nonblockingserver.rb (original)
+++ incubator/thrift/trunk/lib/rb/lib/thrift/server/nonblockingserver.rb Mon Jul  7 17:47:49 2008
@@ -72,6 +72,8 @@
     end
 
     class IOManager # :nodoc:
+      DEFAULT_BUFFER = 2**20
+      
       def initialize(processor, serverTransport, transportFactory, protocolFactory, num, logger)
         @processor = processor
         @serverTransport = serverTransport
@@ -116,7 +118,7 @@
       end
 
       private
-
+      
       def run
         spin_worker_threads
 
@@ -139,7 +141,7 @@
       end
 
       def read_connection(fd)
-        @buffers[fd] << fd.readpartial(1048576)
+        @buffers[fd] << fd.read(DEFAULT_BUFFER)
         frame = slice_frame!(@buffers[fd])
         if frame
           @logger.debug "#{self} is processing a frame"

Modified: incubator/thrift/trunk/lib/rb/lib/thrift/struct.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/lib/thrift/struct.rb?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/rb/lib/thrift/struct.rb (original)
+++ incubator/thrift/trunk/lib/rb/lib/thrift/struct.rb Mon Jul  7 17:47:49 2008
@@ -23,31 +23,41 @@
     end
 
     def read(iprot)
-      iprot.read_struct_begin
-      loop do
-        fname, ftype, fid = iprot.read_field_begin
-        break if (ftype == Types::STOP)
-        handle_message(iprot, fid, ftype)
-        iprot.read_field_end
+      # TODO(kevinclark): Make sure transport is C readable
+      if iprot.respond_to?(:decode_binary)
+        iprot.decode_binary(self, iprot.trans)
+      else
+        iprot.read_struct_begin
+        loop do
+          fname, ftype, fid = iprot.read_field_begin
+          break if (ftype == Types::STOP)
+          handle_message(iprot, fid, ftype)
+          iprot.read_field_end
+        end
+        iprot.read_struct_end
       end
-      iprot.read_struct_end
     end
 
     def write(oprot)
-      oprot.write_struct_begin(self.class.name)
-      each_field do |fid, type, name|
-        unless (value = instance_variable_get("@#{name}")).nil?
-          if is_container? type
-            oprot.write_field_begin(name, type, fid)
-            write_container(oprot, value, struct_fields[fid])
-            oprot.write_field_end
-          else
-            oprot.write_field(name, type, fid, value)
+      if oprot.respond_to?(:encode_binary)
+        # TODO(kevinclark): Clean this so I don't have to access the transport.
+        oprot.trans.write oprot.encode_binary(self)
+      else
+        oprot.write_struct_begin(self.class.name)
+        each_field do |fid, type, name|
+          unless (value = instance_variable_get("@#{name}")).nil?
+            if is_container? type
+              oprot.write_field_begin(name, type, fid)
+              write_container(oprot, value, struct_fields[fid])
+              oprot.write_field_end
+            else
+              oprot.write_field(name, type, fid, value)
+            end
           end
         end
+        oprot.write_field_stop
+        oprot.write_struct_end
       end
-      oprot.write_field_stop
-      oprot.write_struct_end
     end
 
     def ==(other)

Modified: incubator/thrift/trunk/lib/rb/lib/thrift/transport.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/lib/thrift/transport.rb?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/rb/lib/thrift/transport.rb (original)
+++ incubator/thrift/trunk/lib/rb/lib/thrift/transport.rb Mon Jul  7 17:47:49 2008
@@ -76,9 +76,12 @@
   deprecate_class! :TTransportFactory => TransportFactory
 
   class BufferedTransport < Transport
+    DEFAULT_BUFFER = 4096
+    
     def initialize(transport)
       @transport = transport
       @wbuf = ''
+      @rbuf = ''
     end
 
     def open?
@@ -95,7 +98,13 @@
     end
 
     def read(sz)
-      return @transport.read(sz)
+      ret = @rbuf.slice!(0...sz) 
+      if ret.length == 0
+        @rbuf = @transport.read([sz, DEFAULT_BUFFER].max) 
+        @rbuf.slice!(0...sz) 
+      else 
+        ret 
+      end
     end
 
     def write(buf)
@@ -110,6 +119,25 @@
       
       @transport.flush
     end
+
+    def borrow(requested_length = 0)
+      # $stderr.puts "#{Time.now.to_f} Have #{@rbuf.length} asking for #{requested_length.inspect}"
+      return @rbuf if @rbuf.length > requested_length
+      
+      if @rbuf.length < DEFAULT_BUFFER
+        @rbuf << @transport.read([requested_length, DEFAULT_BUFFER].max)
+      end
+      
+      if @rbuf.length < requested_length
+        @rbuf << @transport.read_all(requested_length - @rbuf.length)
+      end
+    
+      @rbuf
+    end
+    
+    def consume!(size)
+      @rbuf.slice!(0...size)
+    end
   end
   deprecate_class! :TBufferedTransport => BufferedTransport
 
@@ -230,6 +258,23 @@
 
     def flush
     end
+
+    # For fast binary protocol access
+    def borrow(size = nil)
+      if size.nil?
+        @buf[0..-1]
+      else
+        if size > @buf.length
+          raise EOFError # Memory buffers only get one shot.
+        else
+          @buf[0..size]
+        end
+      end
+    end
+
+    def consume!(size)
+      @buf.slice!(0, size)
+    end
   end
   deprecate_class! :TMemoryBuffer => MemoryBuffer
 

Modified: incubator/thrift/trunk/lib/rb/lib/thrift/transport/socket.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/lib/thrift/transport/socket.rb?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/rb/lib/thrift/transport/socket.rb (original)
+++ incubator/thrift/trunk/lib/rb/lib/thrift/transport/socket.rb Mon Jul  7 17:47:49 2008
@@ -43,17 +43,11 @@
       end
     end
 
-    def read(sz, partial=false)
+    def read(sz)
       raise IOError, "closed stream" unless open?
+
       begin
-        if partial
-          data = @handle.readpartial(sz)
-        else
-          data = @handle.read(sz)
-        end
-      rescue Errno::EAGAIN => e
-        # let our parent know that the nonblock read failed
-        raise e
+        data = @handle.readpartial(sz)
       rescue StandardError => e
         @handle.close unless @handle.closed?
         @handle = nil
@@ -65,10 +59,6 @@
       data
     end
 
-    def readpartial(sz)
-      read(sz, true)
-    end
-
     def close
       @handle.close unless @handle.nil? or @handle.closed?
       @handle = nil

Added: incubator/thrift/trunk/lib/rb/setup.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/setup.rb?rev=674688&view=auto
==============================================================================
--- incubator/thrift/trunk/lib/rb/setup.rb (added)
+++ incubator/thrift/trunk/lib/rb/setup.rb Mon Jul  7 17:47:49 2008
@@ -0,0 +1,1585 @@
+#
+# setup.rb
+#
+# Copyright (c) 2000-2005 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+#
+
+unless Enumerable.method_defined?(:map)   # Ruby 1.4.6
+  module Enumerable
+    alias map collect
+  end
+end
+
+unless File.respond_to?(:read)   # Ruby 1.6
+  def File.read(fname)
+    open(fname) {|f|
+      return f.read
+    }
+  end
+end
+
+unless Errno.const_defined?(:ENOTEMPTY)   # Windows?
+  module Errno
+    class ENOTEMPTY
+      # We do not raise this exception, implementation is not needed.
+    end
+  end
+end
+
+def File.binread(fname)
+  open(fname, 'rb') {|f|
+    return f.read
+  }
+end
+
+# for corrupted Windows' stat(2)
+def File.dir?(path)
+  File.directory?((path[-1,1] == '/') ? path : path + '/')
+end
+
+
+class ConfigTable
+
+  include Enumerable
+
+  def initialize(rbconfig)
+    @rbconfig = rbconfig
+    @items = []
+    @table = {}
+    # options
+    @install_prefix = nil
+    @config_opt = nil
+    @verbose = true
+    @no_harm = false
+  end
+
+  attr_accessor :install_prefix
+  attr_accessor :config_opt
+
+  attr_writer :verbose
+
+  def verbose?
+    @verbose
+  end
+
+  attr_writer :no_harm
+
+  def no_harm?
+    @no_harm
+  end
+
+  def [](key)
+    lookup(key).resolve(self)
+  end
+
+  def []=(key, val)
+    lookup(key).set val
+  end
+
+  def names
+    @items.map {|i| i.name }
+  end
+
+  def each(&block)
+    @items.each(&block)
+  end
+
+  def key?(name)
+    @table.key?(name)
+  end
+
+  def lookup(name)
+    @table[name] or setup_rb_error "no such config item: #{name}"
+  end
+
+  def add(item)
+    @items.push item
+    @table[item.name] = item
+  end
+
+  def remove(name)
+    item = lookup(name)
+    @items.delete_if {|i| i.name == name }
+    @table.delete_if {|name, i| i.name == name }
+    item
+  end
+
+  def load_script(path, inst = nil)
+    if File.file?(path)
+      MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
+    end
+  end
+
+  def savefile
+    '.config'
+  end
+
+  def load_savefile
+    begin
+      File.foreach(savefile()) do |line|
+        k, v = *line.split(/=/, 2)
+        self[k] = v.strip
+      end
+    rescue Errno::ENOENT
+      setup_rb_error $!.message + "\n#{File.basename($0)} config first"
+    end
+  end
+
+  def save
+    @items.each {|i| i.value }
+    File.open(savefile(), 'w') {|f|
+      @items.each do |i|
+        f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
+      end
+    }
+  end
+
+  def load_standard_entries
+    standard_entries(@rbconfig).each do |ent|
+      add ent
+    end
+  end
+
+  def standard_entries(rbconfig)
+    c = rbconfig
+
+    rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
+
+    major = c['MAJOR'].to_i
+    minor = c['MINOR'].to_i
+    teeny = c['TEENY'].to_i
+    version = "#{major}.#{minor}"
+
+    # ruby ver. >= 1.4.4?
+    newpath_p = ((major >= 2) or
+                 ((major == 1) and
+                  ((minor >= 5) or
+                   ((minor == 4) and (teeny >= 4)))))
+
+    if c['rubylibdir']
+      # V > 1.6.3
+      libruby         = "#{c['prefix']}/lib/ruby"
+      librubyver      = c['rubylibdir']
+      librubyverarch  = c['archdir']
+      siteruby        = c['sitedir']
+      siterubyver     = c['sitelibdir']
+      siterubyverarch = c['sitearchdir']
+    elsif newpath_p
+      # 1.4.4 <= V <= 1.6.3
+      libruby         = "#{c['prefix']}/lib/ruby"
+      librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
+      librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
+      siteruby        = c['sitedir']
+      siterubyver     = "$siteruby/#{version}"
+      siterubyverarch = "$siterubyver/#{c['arch']}"
+    else
+      # V < 1.4.4
+      libruby         = "#{c['prefix']}/lib/ruby"
+      librubyver      = "#{c['prefix']}/lib/ruby/#{version}"
+      librubyverarch  = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
+      siteruby        = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
+      siterubyver     = siteruby
+      siterubyverarch = "$siterubyver/#{c['arch']}"
+    end
+    parameterize = lambda {|path|
+      path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
+    }
+
+    if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
+      makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
+    else
+      makeprog = 'make'
+    end
+
+    [
+      ExecItem.new('installdirs', 'std/site/home',
+                   'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
+          {|val, table|
+            case val
+            when 'std'
+              table['rbdir'] = '$librubyver'
+              table['sodir'] = '$librubyverarch'
+            when 'site'
+              table['rbdir'] = '$siterubyver'
+              table['sodir'] = '$siterubyverarch'
+            when 'home'
+              setup_rb_error '$HOME was not set' unless ENV['HOME']
+              table['prefix'] = ENV['HOME']
+              table['rbdir'] = '$libdir/ruby'
+              table['sodir'] = '$libdir/ruby'
+            end
+          },
+      PathItem.new('prefix', 'path', c['prefix'],
+                   'path prefix of target environment'),
+      PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
+                   'the directory for commands'),
+      PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
+                   'the directory for libraries'),
+      PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
+                   'the directory for shared data'),
+      PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
+                   'the directory for man pages'),
+      PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
+                   'the directory for system configuration files'),
+      PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
+                   'the directory for local state data'),
+      PathItem.new('libruby', 'path', libruby,
+                   'the directory for ruby libraries'),
+      PathItem.new('librubyver', 'path', librubyver,
+                   'the directory for standard ruby libraries'),
+      PathItem.new('librubyverarch', 'path', librubyverarch,
+                   'the directory for standard ruby extensions'),
+      PathItem.new('siteruby', 'path', siteruby,
+          'the directory for version-independent aux ruby libraries'),
+      PathItem.new('siterubyver', 'path', siterubyver,
+                   'the directory for aux ruby libraries'),
+      PathItem.new('siterubyverarch', 'path', siterubyverarch,
+                   'the directory for aux ruby binaries'),
+      PathItem.new('rbdir', 'path', '$siterubyver',
+                   'the directory for ruby scripts'),
+      PathItem.new('sodir', 'path', '$siterubyverarch',
+                   'the directory for ruby extentions'),
+      PathItem.new('rubypath', 'path', rubypath,
+                   'the path to set to #! line'),
+      ProgramItem.new('rubyprog', 'name', rubypath,
+                      'the ruby program using for installation'),
+      ProgramItem.new('makeprog', 'name', makeprog,
+                      'the make program to compile ruby extentions'),
+      SelectItem.new('shebang', 'all/ruby/never', 'ruby',
+                     'shebang line (#!) editing mode'),
+      BoolItem.new('without-ext', 'yes/no', 'no',
+                   'does not compile/install ruby extentions')
+    ]
+  end
+  private :standard_entries
+
+  def load_multipackage_entries
+    multipackage_entries().each do |ent|
+      add ent
+    end
+  end
+
+  def multipackage_entries
+    [
+      PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
+                               'package names that you want to install'),
+      PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
+                               'package names that you do not want to install')
+    ]
+  end
+  private :multipackage_entries
+
+  ALIASES = {
+    'std-ruby'         => 'librubyver',
+    'stdruby'          => 'librubyver',
+    'rubylibdir'       => 'librubyver',
+    'archdir'          => 'librubyverarch',
+    'site-ruby-common' => 'siteruby',     # For backward compatibility
+    'site-ruby'        => 'siterubyver',  # For backward compatibility
+    'bin-dir'          => 'bindir',
+    'bin-dir'          => 'bindir',
+    'rb-dir'           => 'rbdir',
+    'so-dir'           => 'sodir',
+    'data-dir'         => 'datadir',
+    'ruby-path'        => 'rubypath',
+    'ruby-prog'        => 'rubyprog',
+    'ruby'             => 'rubyprog',
+    'make-prog'        => 'makeprog',
+    'make'             => 'makeprog'
+  }
+
+  def fixup
+    ALIASES.each do |ali, name|
+      @table[ali] = @table[name]
+    end
+    @items.freeze
+    @table.freeze
+    @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
+  end
+
+  def parse_opt(opt)
+    m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
+    m.to_a[1,2]
+  end
+
+  def dllext
+    @rbconfig['DLEXT']
+  end
+
+  def value_config?(name)
+    lookup(name).value?
+  end
+
+  class Item
+    def initialize(name, template, default, desc)
+      @name = name.freeze
+      @template = template
+      @value = default
+      @default = default
+      @description = desc
+    end
+
+    attr_reader :name
+    attr_reader :description
+
+    attr_accessor :default
+    alias help_default default
+
+    def help_opt
+      "--#{@name}=#{@template}"
+    end
+
+    def value?
+      true
+    end
+
+    def value
+      @value
+    end
+
+    def resolve(table)
+      @value.gsub(%r<\$([^/]+)>) { table[$1] }
+    end
+
+    def set(val)
+      @value = check(val)
+    end
+
+    private
+
+    def check(val)
+      setup_rb_error "config: --#{name} requires argument" unless val
+      val
+    end
+  end
+
+  class BoolItem < Item
+    def config_type
+      'bool'
+    end
+
+    def help_opt
+      "--#{@name}"
+    end
+
+    private
+
+    def check(val)
+      return 'yes' unless val
+      case val
+      when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
+      when /\An(o)?\z/i, /\Af(alse)\z/i  then 'no'
+      else
+        setup_rb_error "config: --#{@name} accepts only yes/no for argument"
+      end
+    end
+  end
+
+  class PathItem < Item
+    def config_type
+      'path'
+    end
+
+    private
+
+    def check(path)
+      setup_rb_error "config: --#{@name} requires argument"  unless path
+      path[0,1] == '$' ? path : File.expand_path(path)
+    end
+  end
+
+  class ProgramItem < Item
+    def config_type
+      'program'
+    end
+  end
+
+  class SelectItem < Item
+    def initialize(name, selection, default, desc)
+      super
+      @ok = selection.split('/')
+    end
+
+    def config_type
+      'select'
+    end
+
+    private
+
+    def check(val)
+      unless @ok.include?(val.strip)
+        setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
+      end
+      val.strip
+    end
+  end
+
+  class ExecItem < Item
+    def initialize(name, selection, desc, &block)
+      super name, selection, nil, desc
+      @ok = selection.split('/')
+      @action = block
+    end
+
+    def config_type
+      'exec'
+    end
+
+    def value?
+      false
+    end
+
+    def resolve(table)
+      setup_rb_error "$#{name()} wrongly used as option value"
+    end
+
+    undef set
+
+    def evaluate(val, table)
+      v = val.strip.downcase
+      unless @ok.include?(v)
+        setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
+      end
+      @action.call v, table
+    end
+  end
+
+  class PackageSelectionItem < Item
+    def initialize(name, template, default, help_default, desc)
+      super name, template, default, desc
+      @help_default = help_default
+    end
+
+    attr_reader :help_default
+
+    def config_type
+      'package'
+    end
+
+    private
+
+    def check(val)
+      unless File.dir?("packages/#{val}")
+        setup_rb_error "config: no such package: #{val}"
+      end
+      val
+    end
+  end
+
+  class MetaConfigEnvironment
+    def initialize(config, installer)
+      @config = config
+      @installer = installer
+    end
+
+    def config_names
+      @config.names
+    end
+
+    def config?(name)
+      @config.key?(name)
+    end
+
+    def bool_config?(name)
+      @config.lookup(name).config_type == 'bool'
+    end
+
+    def path_config?(name)
+      @config.lookup(name).config_type == 'path'
+    end
+
+    def value_config?(name)
+      @config.lookup(name).config_type != 'exec'
+    end
+
+    def add_config(item)
+      @config.add item
+    end
+
+    def add_bool_config(name, default, desc)
+      @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+    end
+
+    def add_path_config(name, default, desc)
+      @config.add PathItem.new(name, 'path', default, desc)
+    end
+
+    def set_config_default(name, default)
+      @config.lookup(name).default = default
+    end
+
+    def remove_config(name)
+      @config.remove(name)
+    end
+
+    # For only multipackage
+    def packages
+      raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
+      @installer.packages
+    end
+
+    # For only multipackage
+    def declare_packages(list)
+      raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
+      @installer.packages = list
+    end
+  end
+
+end   # class ConfigTable
+
+
+# This module requires: #verbose?, #no_harm?
+module FileOperations
+
+  def mkdir_p(dirname, prefix = nil)
+    dirname = prefix + File.expand_path(dirname) if prefix
+    $stderr.puts "mkdir -p #{dirname}" if verbose?
+    return if no_harm?
+
+    # Does not check '/', it's too abnormal.
+    dirs = File.expand_path(dirname).split(%r<(?=/)>)
+    if /\A[a-z]:\z/i =~ dirs[0]
+      disk = dirs.shift
+      dirs[0] = disk + dirs[0]
+    end
+    dirs.each_index do |idx|
+      path = dirs[0..idx].join('')
+      Dir.mkdir path unless File.dir?(path)
+    end
+  end
+
+  def rm_f(path)
+    $stderr.puts "rm -f #{path}" if verbose?
+    return if no_harm?
+    force_remove_file path
+  end
+
+  def rm_rf(path)
+    $stderr.puts "rm -rf #{path}" if verbose?
+    return if no_harm?
+    remove_tree path
+  end
+
+  def remove_tree(path)
+    if File.symlink?(path)
+      remove_file path
+    elsif File.dir?(path)
+      remove_tree0 path
+    else
+      force_remove_file path
+    end
+  end
+
+  def remove_tree0(path)
+    Dir.foreach(path) do |ent|
+      next if ent == '.'
+      next if ent == '..'
+      entpath = "#{path}/#{ent}"
+      if File.symlink?(entpath)
+        remove_file entpath
+      elsif File.dir?(entpath)
+        remove_tree0 entpath
+      else
+        force_remove_file entpath
+      end
+    end
+    begin
+      Dir.rmdir path
+    rescue Errno::ENOTEMPTY
+      # directory may not be empty
+    end
+  end
+
+  def move_file(src, dest)
+    force_remove_file dest
+    begin
+      File.rename src, dest
+    rescue
+      File.open(dest, 'wb') {|f|
+        f.write File.binread(src)
+      }
+      File.chmod File.stat(src).mode, dest
+      File.unlink src
+    end
+  end
+
+  def force_remove_file(path)
+    begin
+      remove_file path
+    rescue
+    end
+  end
+
+  def remove_file(path)
+    File.chmod 0777, path
+    File.unlink path
+  end
+
+  def install(from, dest, mode, prefix = nil)
+    $stderr.puts "install #{from} #{dest}" if verbose?
+    return if no_harm?
+
+    realdest = prefix ? prefix + File.expand_path(dest) : dest
+    realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
+    str = File.binread(from)
+    if diff?(str, realdest)
+      verbose_off {
+        rm_f realdest if File.exist?(realdest)
+      }
+      File.open(realdest, 'wb') {|f|
+        f.write str
+      }
+      File.chmod mode, realdest
+
+      File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
+        if prefix
+          f.puts realdest.sub(prefix, '')
+        else
+          f.puts realdest
+        end
+      }
+    end
+  end
+
+  def diff?(new_content, path)
+    return true unless File.exist?(path)
+    new_content != File.binread(path)
+  end
+
+  def command(*args)
+    $stderr.puts args.join(' ') if verbose?
+    system(*args) or raise RuntimeError,
+        "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
+  end
+
+  def ruby(*args)
+    command config('rubyprog'), *args
+  end
+
+  def make(task = nil)
+    command(*[config('makeprog'), task].compact)
+  end
+
+  def extdir?(dir)
+    File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
+  end
+
+  def files_of(dir)
+    Dir.open(dir) {|d|
+      return d.select {|ent| File.file?("#{dir}/#{ent}") }
+    }
+  end
+
+  DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
+
+  def directories_of(dir)
+    Dir.open(dir) {|d|
+      return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
+    }
+  end
+
+end
+
+
+# This module requires: #srcdir_root, #objdir_root, #relpath
+module HookScriptAPI
+
+  def get_config(key)
+    @config[key]
+  end
+
+  alias config get_config
+
+  # obsolete: use metaconfig to change configuration
+  def set_config(key, val)
+    @config[key] = val
+  end
+
+  #
+  # srcdir/objdir (works only in the package directory)
+  #
+
+  def curr_srcdir
+    "#{srcdir_root()}/#{relpath()}"
+  end
+
+  def curr_objdir
+    "#{objdir_root()}/#{relpath()}"
+  end
+
+  def srcfile(path)
+    "#{curr_srcdir()}/#{path}"
+  end
+
+  def srcexist?(path)
+    File.exist?(srcfile(path))
+  end
+
+  def srcdirectory?(path)
+    File.dir?(srcfile(path))
+  end
+
+  def srcfile?(path)
+    File.file?(srcfile(path))
+  end
+
+  def srcentries(path = '.')
+    Dir.open("#{curr_srcdir()}/#{path}") {|d|
+      return d.to_a - %w(. ..)
+    }
+  end
+
+  def srcfiles(path = '.')
+    srcentries(path).select {|fname|
+      File.file?(File.join(curr_srcdir(), path, fname))
+    }
+  end
+
+  def srcdirectories(path = '.')
+    srcentries(path).select {|fname|
+      File.dir?(File.join(curr_srcdir(), path, fname))
+    }
+  end
+
+end
+
+
+class ToplevelInstaller
+
+  Version   = '3.4.1'
+  Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
+
+  TASKS = [
+    [ 'all',      'do config, setup, then install' ],
+    [ 'config',   'saves your configurations' ],
+    [ 'show',     'shows current configuration' ],
+    [ 'setup',    'compiles ruby extentions and others' ],
+    [ 'install',  'installs files' ],
+    [ 'test',     'run all tests in test/' ],
+    [ 'clean',    "does `make clean' for each extention" ],
+    [ 'distclean',"does `make distclean' for each extention" ]
+  ]
+
+  def ToplevelInstaller.invoke
+    config = ConfigTable.new(load_rbconfig())
+    config.load_standard_entries
+    config.load_multipackage_entries if multipackage?
+    config.fixup
+    klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
+    klass.new(File.dirname($0), config).invoke
+  end
+
+  def ToplevelInstaller.multipackage?
+    File.dir?(File.dirname($0) + '/packages')
+  end
+
+  def ToplevelInstaller.load_rbconfig
+    if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
+      ARGV.delete(arg)
+      load File.expand_path(arg.split(/=/, 2)[1])
+      $".push 'rbconfig.rb'
+    else
+      require 'rbconfig'
+    end
+    ::Config::CONFIG
+  end
+
+  def initialize(ardir_root, config)
+    @ardir = File.expand_path(ardir_root)
+    @config = config
+    # cache
+    @valid_task_re = nil
+  end
+
+  def config(key)
+    @config[key]
+  end
+
+  def inspect
+    "#<#{self.class} #{__id__()}>"
+  end
+
+  def invoke
+    run_metaconfigs
+    case task = parsearg_global()
+    when nil, 'all'
+      parsearg_config
+      init_installers
+      exec_config
+      exec_setup
+      exec_install
+    else
+      case task
+      when 'config', 'test'
+        ;
+      when 'clean', 'distclean'
+        @config.load_savefile if File.exist?(@config.savefile)
+      else
+        @config.load_savefile
+      end
+      __send__ "parsearg_#{task}"
+      init_installers
+      __send__ "exec_#{task}"
+    end
+  end
+
+  def run_metaconfigs
+    @config.load_script "#{@ardir}/metaconfig"
+  end
+
+  def init_installers
+    @installer = Installer.new(@config, @ardir, File.expand_path('.'))
+  end
+
+  #
+  # Hook Script API bases
+  #
+
+  def srcdir_root
+    @ardir
+  end
+
+  def objdir_root
+    '.'
+  end
+
+  def relpath
+    '.'
+  end
+
+  #
+  # Option Parsing
+  #
+
+  def parsearg_global
+    while arg = ARGV.shift
+      case arg
+      when /\A\w+\z/
+        setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
+        return arg
+      when '-q', '--quiet'
+        @config.verbose = false
+      when '--verbose'
+        @config.verbose = true
+      when '--help'
+        print_usage $stdout
+        exit 0
+      when '--version'
+        puts "#{File.basename($0)} version #{Version}"
+        exit 0
+      when '--copyright'
+        puts Copyright
+        exit 0
+      else
+        setup_rb_error "unknown global option '#{arg}'"
+      end
+    end
+    nil
+  end
+
+  def valid_task?(t)
+    valid_task_re() =~ t
+  end
+
+  def valid_task_re
+    @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
+  end
+
+  def parsearg_no_options
+    unless ARGV.empty?
+      task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
+      setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
+    end
+  end
+
+  alias parsearg_show       parsearg_no_options
+  alias parsearg_setup      parsearg_no_options
+  alias parsearg_test       parsearg_no_options
+  alias parsearg_clean      parsearg_no_options
+  alias parsearg_distclean  parsearg_no_options
+
+  def parsearg_config
+    evalopt = []
+    set = []
+    @config.config_opt = []
+    while i = ARGV.shift
+      if /\A--?\z/ =~ i
+        @config.config_opt = ARGV.dup
+        break
+      end
+      name, value = *@config.parse_opt(i)
+      if @config.value_config?(name)
+        @config[name] = value
+      else
+        evalopt.push [name, value]
+      end
+      set.push name
+    end
+    evalopt.each do |name, value|
+      @config.lookup(name).evaluate value, @config
+    end
+    # Check if configuration is valid
+    set.each do |n|
+      @config[n] if @config.value_config?(n)
+    end
+  end
+
+  def parsearg_install
+    @config.no_harm = false
+    @config.install_prefix = ''
+    while a = ARGV.shift
+      case a
+      when '--no-harm'
+        @config.no_harm = true
+      when /\A--prefix=/
+        path = a.split(/=/, 2)[1]
+        path = File.expand_path(path) unless path[0,1] == '/'
+        @config.install_prefix = path
+      else
+        setup_rb_error "install: unknown option #{a}"
+      end
+    end
+  end
+
+  def print_usage(out)
+    out.puts 'Typical Installation Procedure:'
+    out.puts "  $ ruby #{File.basename $0} config"
+    out.puts "  $ ruby #{File.basename $0} setup"
+    out.puts "  # ruby #{File.basename $0} install (may require root privilege)"
+    out.puts
+    out.puts 'Detailed Usage:'
+    out.puts "  ruby #{File.basename $0} <global option>"
+    out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"
+
+    fmt = "  %-24s %s\n"
+    out.puts
+    out.puts 'Global options:'
+    out.printf fmt, '-q,--quiet',   'suppress message outputs'
+    out.printf fmt, '   --verbose', 'output messages verbosely'
+    out.printf fmt, '   --help',    'print this message'
+    out.printf fmt, '   --version', 'print version and quit'
+    out.printf fmt, '   --copyright',  'print copyright and quit'
+    out.puts
+    out.puts 'Tasks:'
+    TASKS.each do |name, desc|
+      out.printf fmt, name, desc
+    end
+
+    fmt = "  %-24s %s [%s]\n"
+    out.puts
+    out.puts 'Options for CONFIG or ALL:'
+    @config.each do |item|
+      out.printf fmt, item.help_opt, item.description, item.help_default
+    end
+    out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
+    out.puts
+    out.puts 'Options for INSTALL:'
+    out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
+    out.printf fmt, '--prefix=path',  'install path prefix', ''
+    out.puts
+  end
+
+  #
+  # Task Handlers
+  #
+
+  def exec_config
+    @installer.exec_config
+    @config.save   # must be final
+  end
+
+  def exec_setup
+    @installer.exec_setup
+  end
+
+  def exec_install
+    @installer.exec_install
+  end
+
+  def exec_test
+    @installer.exec_test
+  end
+
+  def exec_show
+    @config.each do |i|
+      printf "%-20s %s\n", i.name, i.value if i.value?
+    end
+  end
+
+  def exec_clean
+    @installer.exec_clean
+  end
+
+  def exec_distclean
+    @installer.exec_distclean
+  end
+
+end   # class ToplevelInstaller
+
+
+class ToplevelInstallerMulti < ToplevelInstaller
+
+  include FileOperations
+
+  def initialize(ardir_root, config)
+    super
+    @packages = directories_of("#{@ardir}/packages")
+    raise 'no package exists' if @packages.empty?
+    @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
+  end
+
+  def run_metaconfigs
+    @config.load_script "#{@ardir}/metaconfig", self
+    @packages.each do |name|
+      @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
+    end
+  end
+
+  attr_reader :packages
+
+  def packages=(list)
+    raise 'package list is empty' if list.empty?
+    list.each do |name|
+      raise "directory packages/#{name} does not exist"\
+              unless File.dir?("#{@ardir}/packages/#{name}")
+    end
+    @packages = list
+  end
+
+  def init_installers
+    @installers = {}
+    @packages.each do |pack|
+      @installers[pack] = Installer.new(@config,
+                                       "#{@ardir}/packages/#{pack}",
+                                       "packages/#{pack}")
+    end
+    with    = extract_selection(config('with'))
+    without = extract_selection(config('without'))
+    @selected = @installers.keys.select {|name|
+                  (with.empty? or with.include?(name)) \
+                      and not without.include?(name)
+                }
+  end
+
+  def extract_selection(list)
+    a = list.split(/,/)
+    a.each do |name|
+      setup_rb_error "no such package: #{name}"  unless @installers.key?(name)
+    end
+    a
+  end
+
+  def print_usage(f)
+    super
+    f.puts 'Inluded packages:'
+    f.puts '  ' + @packages.sort.join(' ')
+    f.puts
+  end
+
+  #
+  # Task Handlers
+  #
+
+  def exec_config
+    run_hook 'pre-config'
+    each_selected_installers {|inst| inst.exec_config }
+    run_hook 'post-config'
+    @config.save   # must be final
+  end
+
+  def exec_setup
+    run_hook 'pre-setup'
+    each_selected_installers {|inst| inst.exec_setup }
+    run_hook 'post-setup'
+  end
+
+  def exec_install
+    run_hook 'pre-install'
+    each_selected_installers {|inst| inst.exec_install }
+    run_hook 'post-install'
+  end
+
+  def exec_test
+    run_hook 'pre-test'
+    each_selected_installers {|inst| inst.exec_test }
+    run_hook 'post-test'
+  end
+
+  def exec_clean
+    rm_f @config.savefile
+    run_hook 'pre-clean'
+    each_selected_installers {|inst| inst.exec_clean }
+    run_hook 'post-clean'
+  end
+
+  def exec_distclean
+    rm_f @config.savefile
+    run_hook 'pre-distclean'
+    each_selected_installers {|inst| inst.exec_distclean }
+    run_hook 'post-distclean'
+  end
+
+  #
+  # lib
+  #
+
+  def each_selected_installers
+    Dir.mkdir 'packages' unless File.dir?('packages')
+    @selected.each do |pack|
+      $stderr.puts "Processing the package `#{pack}' ..." if verbose?
+      Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
+      Dir.chdir "packages/#{pack}"
+      yield @installers[pack]
+      Dir.chdir '../..'
+    end
+  end
+
+  def run_hook(id)
+    @root_installer.run_hook id
+  end
+
+  # module FileOperations requires this
+  def verbose?
+    @config.verbose?
+  end
+
+  # module FileOperations requires this
+  def no_harm?
+    @config.no_harm?
+  end
+
+end   # class ToplevelInstallerMulti
+
+
+class Installer
+
+  FILETYPES = %w( bin lib ext data conf man )
+
+  include FileOperations
+  include HookScriptAPI
+
+  def initialize(config, srcroot, objroot)
+    @config = config
+    @srcdir = File.expand_path(srcroot)
+    @objdir = File.expand_path(objroot)
+    @currdir = '.'
+  end
+
+  def inspect
+    "#<#{self.class} #{File.basename(@srcdir)}>"
+  end
+
+  def noop(rel)
+  end
+
+  #
+  # Hook Script API base methods
+  #
+
+  def srcdir_root
+    @srcdir
+  end
+
+  def objdir_root
+    @objdir
+  end
+
+  def relpath
+    @currdir
+  end
+
+  #
+  # Config Access
+  #
+
+  # module FileOperations requires this
+  def verbose?
+    @config.verbose?
+  end
+
+  # module FileOperations requires this
+  def no_harm?
+    @config.no_harm?
+  end
+
+  def verbose_off
+    begin
+      save, @config.verbose = @config.verbose?, false
+      yield
+    ensure
+      @config.verbose = save
+    end
+  end
+
+  #
+  # TASK config
+  #
+
+  def exec_config
+    exec_task_traverse 'config'
+  end
+
+  alias config_dir_bin noop
+  alias config_dir_lib noop
+
+  def config_dir_ext(rel)
+    extconf if extdir?(curr_srcdir())
+  end
+
+  alias config_dir_data noop
+  alias config_dir_conf noop
+  alias config_dir_man noop
+
+  def extconf
+    ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
+  end
+
+  #
+  # TASK setup
+  #
+
+  def exec_setup
+    exec_task_traverse 'setup'
+  end
+
+  def setup_dir_bin(rel)
+    files_of(curr_srcdir()).each do |fname|
+      update_shebang_line "#{curr_srcdir()}/#{fname}"
+    end
+  end
+
+  alias setup_dir_lib noop
+
+  def setup_dir_ext(rel)
+    make if extdir?(curr_srcdir())
+  end
+
+  alias setup_dir_data noop
+  alias setup_dir_conf noop
+  alias setup_dir_man noop
+
+  def update_shebang_line(path)
+    return if no_harm?
+    return if config('shebang') == 'never'
+    old = Shebang.load(path)
+    if old
+      $stderr.puts "warning: #{path}: Shebang line includes too many args.  It is not portable and your program may not work." if old.args.size > 1
+      new = new_shebang(old)
+      return if new.to_s == old.to_s
+    else
+      return unless config('shebang') == 'all'
+      new = Shebang.new(config('rubypath'))
+    end
+    $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
+    open_atomic_writer(path) {|output|
+      File.open(path, 'rb') {|f|
+        f.gets if old   # discard
+        output.puts new.to_s
+        output.print f.read
+      }
+    }
+  end
+
+  def new_shebang(old)
+    if /\Aruby/ =~ File.basename(old.cmd)
+      Shebang.new(config('rubypath'), old.args)
+    elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
+      Shebang.new(config('rubypath'), old.args[1..-1])
+    else
+      return old unless config('shebang') == 'all'
+      Shebang.new(config('rubypath'))
+    end
+  end
+
+  def open_atomic_writer(path, &block)
+    tmpfile = File.basename(path) + '.tmp'
+    begin
+      File.open(tmpfile, 'wb', &block)
+      File.rename tmpfile, File.basename(path)
+    ensure
+      File.unlink tmpfile if File.exist?(tmpfile)
+    end
+  end
+
+  class Shebang
+    def Shebang.load(path)
+      line = nil
+      File.open(path) {|f|
+        line = f.gets
+      }
+      return nil unless /\A#!/ =~ line
+      parse(line)
+    end
+
+    def Shebang.parse(line)
+      cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
+      new(cmd, args)
+    end
+
+    def initialize(cmd, args = [])
+      @cmd = cmd
+      @args = args
+    end
+
+    attr_reader :cmd
+    attr_reader :args
+
+    def to_s
+      "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
+    end
+  end
+
+  #
+  # TASK install
+  #
+
+  def exec_install
+    rm_f 'InstalledFiles'
+    exec_task_traverse 'install'
+  end
+
+  def install_dir_bin(rel)
+    install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
+  end
+
+  def install_dir_lib(rel)
+    install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
+  end
+
+  def install_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    install_files rubyextentions('.'),
+                  "#{config('sodir')}/#{File.dirname(rel)}",
+                  0555
+  end
+
+  def install_dir_data(rel)
+    install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
+  end
+
+  def install_dir_conf(rel)
+    # FIXME: should not remove current config files
+    # (rename previous file to .old/.org)
+    install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
+  end
+
+  def install_dir_man(rel)
+    install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
+  end
+
+  def install_files(list, dest, mode)
+    mkdir_p dest, @config.install_prefix
+    list.each do |fname|
+      install fname, dest, mode, @config.install_prefix
+    end
+  end
+
+  def libfiles
+    glob_reject(%w(*.y *.output), targetfiles())
+  end
+
+  def rubyextentions(dir)
+    ents = glob_select("*.#{@config.dllext}", targetfiles())
+    if ents.empty?
+      setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
+    end
+    ents
+  end
+
+  def targetfiles
+    mapdir(existfiles() - hookfiles())
+  end
+
+  def mapdir(ents)
+    ents.map {|ent|
+      if File.exist?(ent)
+      then ent                         # objdir
+      else "#{curr_srcdir()}/#{ent}"   # srcdir
+      end
+    }
+  end
+
+  # picked up many entries from cvs-1.11.1/src/ignore.c
+  JUNK_FILES = %w(
+    core RCSLOG tags TAGS .make.state
+    .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
+    *~ *.old *.bak *.BAK *.orig *.rej _$* *$
+
+    *.org *.in .*
+  )
+
+  def existfiles
+    glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
+  end
+
+  def hookfiles
+    %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
+      %w( config setup install clean ).map {|t| sprintf(fmt, t) }
+    }.flatten
+  end
+
+  def glob_select(pat, ents)
+    re = globs2re([pat])
+    ents.select {|ent| re =~ ent }
+  end
+
+  def glob_reject(pats, ents)
+    re = globs2re(pats)
+    ents.reject {|ent| re =~ ent }
+  end
+
+  GLOB2REGEX = {
+    '.' => '\.',
+    '$' => '\$',
+    '#' => '\#',
+    '*' => '.*'
+  }
+
+  def globs2re(pats)
+    /\A(?:#{
+      pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
+    })\z/
+  end
+
+  #
+  # TASK test
+  #
+
+  TESTDIR = 'test'
+
+  def exec_test
+    unless File.directory?('test')
+      $stderr.puts 'no test in this package' if verbose?
+      return
+    end
+    $stderr.puts 'Running tests...' if verbose?
+    begin
+      require 'test/unit'
+    rescue LoadError
+      setup_rb_error 'test/unit cannot loaded.  You need Ruby 1.8 or later to invoke this task.'
+    end
+    runner = Test::Unit::AutoRunner.new(true)
+    runner.to_run << TESTDIR
+    runner.run
+  end
+
+  #
+  # TASK clean
+  #
+
+  def exec_clean
+    exec_task_traverse 'clean'
+    rm_f @config.savefile
+    rm_f 'InstalledFiles'
+  end
+
+  alias clean_dir_bin noop
+  alias clean_dir_lib noop
+  alias clean_dir_data noop
+  alias clean_dir_conf noop
+  alias clean_dir_man noop
+
+  def clean_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    make 'clean' if File.file?('Makefile')
+  end
+
+  #
+  # TASK distclean
+  #
+
+  def exec_distclean
+    exec_task_traverse 'distclean'
+    rm_f @config.savefile
+    rm_f 'InstalledFiles'
+  end
+
+  alias distclean_dir_bin noop
+  alias distclean_dir_lib noop
+
+  def distclean_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    make 'distclean' if File.file?('Makefile')
+  end
+
+  alias distclean_dir_data noop
+  alias distclean_dir_conf noop
+  alias distclean_dir_man noop
+
+  #
+  # Traversing
+  #
+
+  def exec_task_traverse(task)
+    run_hook "pre-#{task}"
+    FILETYPES.each do |type|
+      if type == 'ext' and config('without-ext') == 'yes'
+        $stderr.puts 'skipping ext/* by user option' if verbose?
+        next
+      end
+      traverse task, type, "#{task}_dir_#{type}"
+    end
+    run_hook "post-#{task}"
+  end
+
+  def traverse(task, rel, mid)
+    dive_into(rel) {
+      run_hook "pre-#{task}"
+      __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
+      directories_of(curr_srcdir()).each do |d|
+        traverse task, "#{rel}/#{d}", mid
+      end
+      run_hook "post-#{task}"
+    }
+  end
+
+  def dive_into(rel)
+    return unless File.dir?("#{@srcdir}/#{rel}")
+
+    dir = File.basename(rel)
+    Dir.mkdir dir unless File.dir?(dir)
+    prevdir = Dir.pwd
+    Dir.chdir dir
+    $stderr.puts '---> ' + rel if verbose?
+    @currdir = rel
+    yield
+    Dir.chdir prevdir
+    $stderr.puts '<--- ' + rel if verbose?
+    @currdir = File.dirname(rel)
+  end
+
+  def run_hook(id)
+    path = [ "#{curr_srcdir()}/#{id}",
+             "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
+    return unless path
+    begin
+      instance_eval File.read(path), path, 1
+    rescue
+      raise if $DEBUG
+      setup_rb_error "hook #{path} failed:\n" + $!.message
+    end
+  end
+
+end   # class Installer
+
+
+class SetupError < StandardError; end
+
+def setup_rb_error(msg)
+  raise SetupError, msg
+end
+
+if $0 == __FILE__
+  begin
+    ToplevelInstaller.invoke
+  rescue SetupError
+    raise if $DEBUG
+    $stderr.puts $!.message
+    $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
+    exit 1
+  end
+end

Modified: incubator/thrift/trunk/lib/rb/spec/socket_spec_shared.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/spec/socket_spec_shared.rb?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/rb/spec/socket_spec_shared.rb (original)
+++ incubator/thrift/trunk/lib/rb/spec/socket_spec_shared.rb Mon Jul  7 17:47:49 2008
@@ -26,19 +26,13 @@
 
   it "should raise an error when it cannot read from the handle" do
     @socket.open
-    @handle.should_receive(:read).with(17).and_raise(StandardError)
+    @handle.should_receive(:readpartial).with(17).and_raise(StandardError)
     lambda { @socket.read(17) }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN }
   end
 
-  it "should raise an error when it reads no data from the handle" do
-    @socket.open
-    @handle.should_receive(:read).with(17).and_return("")
-    lambda { @socket.read(17) }.should raise_error(Thrift::TransportException, "Socket: Could not read 17 bytes from #{@socket.instance_variable_get("@desc")}")
-  end
-
   it "should return the data read when reading from the handle works" do
     @socket.open
-    @handle.should_receive(:read).with(17).and_return("test data")
+    @handle.should_receive(:readpartial).with(17).and_return("test data")
     @socket.read(17).should == "test data"
   end
 

Modified: incubator/thrift/trunk/lib/rb/spec/transport_spec.rb
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/lib/rb/spec/transport_spec.rb?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/lib/rb/spec/transport_spec.rb (original)
+++ incubator/thrift/trunk/lib/rb/spec/transport_spec.rb Mon Jul  7 17:47:49 2008
@@ -48,18 +48,26 @@
   end
 
   describe BufferedTransport do
-    it "should pass through everything but write/flush" do
+    it "should pass through everything but write/flush/read" do
       trans = mock("Transport")
       trans.should_receive(:open?).ordered.and_return("+ open?")
       trans.should_receive(:open).ordered.and_return("+ open")
       trans.should_receive(:flush).ordered # from the close
       trans.should_receive(:close).ordered.and_return("+ close")
-      trans.should_receive(:read).with(217).ordered.and_return("+ read")
       btrans = BufferedTransport.new(trans)
       btrans.open?.should == "+ open?"
       btrans.open.should == "+ open"
       btrans.close.should == "+ close"
-      btrans.read(217).should == "+ read"
+    end
+    
+    it "should buffer reads in chunks of #{BufferedTransport::DEFAULT_BUFFER}" do
+      trans = mock("Transport")
+      trans.should_receive(:read).with(BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet")
+      btrans = BufferedTransport.new(trans)
+      btrans.read(6).should == "lorum "
+      btrans.read(6).should == "ipsum "
+      btrans.read(6).should == "dolor "
+      btrans.read(6).should == "emet"
     end
 
     it "should buffer writes and send them on flush" do

Modified: incubator/thrift/trunk/test/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/test/Makefile.am?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/test/Makefile.am (original)
+++ incubator/thrift/trunk/test/Makefile.am Mon Jul  7 17:47:49 2008
@@ -8,6 +8,10 @@
 SUBDIRS += py
 endif
 
+if ENABLE_RUBY
+SUBDIRS += rb
+endif
+
 noinst_LTLIBRARIES = libtestgencpp.la
 libtestgencpp_la_SOURCES = \
 	gen-cpp/DebugProtoTest_types.cpp \

Modified: incubator/thrift/trunk/test/SmallTest.thrift
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/test/SmallTest.thrift?rev=674688&r1=674687&r2=674688&view=diff
==============================================================================
--- incubator/thrift/trunk/test/SmallTest.thrift (original)
+++ incubator/thrift/trunk/test/SmallTest.thrift Mon Jul  7 17:47:49 2008
@@ -11,6 +11,10 @@
   "ASDFLJASDF"
 }
 
+struct BoolPasser {
+  1: bool value = 1
+}
+
 struct Hello {
   1: i32 simple = 53,
   2: map<i32,i32> complex = {23:532, 6243:632, 2355:532},

Added: incubator/thrift/trunk/test/rb/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/thrift/trunk/test/rb/Makefile.am?rev=674688&view=auto
==============================================================================
--- incubator/thrift/trunk/test/rb/Makefile.am (added)
+++ incubator/thrift/trunk/test/rb/Makefile.am Mon Jul  7 17:47:49 2008
@@ -0,0 +1,9 @@
+THRIFT = $(top_srcdir)/compiler/cpp/thrift
+
+stubs: ../ThriftTest.thrift ../SmallTest.thrift
+	$(THRIFT) --gen rb ../ThriftTest.thrift
+	$(THRIFT) --gen rb ../SmallTest.thrift
+
+check: stubs
+	$(RUBY) test_suite.rb
+