diff --git a/.gitignore b/.gitignore index c33abaf..9f998c4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ pkg doc mongo-ruby-driver-*.gem nbproject +*.bundle +*.o +ext/cbson/Makefile diff --git a/Rakefile b/Rakefile index f236966..4b3befe 100644 --- a/Rakefile +++ b/Rakefile @@ -4,7 +4,10 @@ require 'fileutils' require 'rake' require 'rake/testtask' require 'rake/gempackagetask' -require 'rake/contrib/rubyforgepublisher' +begin + require 'rake/contrib/rubyforgepublisher' +rescue LoadError +end # NOTE: some of the tests assume Mongo is running Rake::TestTask.new do |t| @@ -23,6 +26,14 @@ task :publish => [:rdoc] do Rake::RubyForgePublisher.new(GEM, RUBYFORGE_USER).upload end +desc "Compile the extension" +task :compile do + cd 'ext/cbson' + ruby 'extconf.rb' + sh 'make' + cp 'cbson.bundle', '../../lib/mongo/ext/cbson.bundle' +end + namespace :gem do desc "Install the gem locally" diff --git a/ext/cbson/cbson.c b/ext/cbson/cbson.c new file mode 100644 index 0000000..071fb9b --- /dev/null +++ b/ext/cbson/cbson.c @@ -0,0 +1,76 @@ +#include "ruby.h" +#include + +#define INITIAL_BUFFER_SIZE 256 + +typedef struct { + char* buffer; + int size; + int position; +} bson_buffer; + +static bson_buffer* buffer_new(void) { + bson_buffer* buffer; + buffer = (bson_buffer*)malloc(sizeof(bson_buffer)); + assert(buffer); + + buffer->size = INITIAL_BUFFER_SIZE; + buffer->position = 0; + buffer->buffer = (char*)malloc(INITIAL_BUFFER_SIZE); + assert(buffer->buffer); + + return buffer; +} + +static void buffer_free(bson_buffer* buffer) { + assert(buffer); + assert(buffer->buffer); + + free(buffer->buffer); + free(buffer); +} + +static void buffer_resize(bson_buffer* buffer, int min_length) { + int size = buffer->size; + if (size >= min_length) { + return; + } + while (size < min_length) { + size *= 2; + } + buffer->buffer = (char*)realloc(buffer->buffer, size); + assert(buffer->buffer); + buffer->size = size; +} + +static void buffer_assure_space(bson_buffer* buffer, int size) { + if (buffer->position + size <= buffer->size) { + return; + } + buffer_resize(buffer, buffer->position + size); +} + +/* returns offset for writing */ +static int buffer_save_bytes(bson_buffer* buffer, int size) { + buffer_assure_space(buffer, size); + int position = buffer->position; + buffer->position += size; + return position; +} + +static void buffer_write_bytes(bson_buffer* buffer, const char* bytes, int size) { + buffer_assure_space(buffer, size); + + memcpy(buffer->buffer + buffer->position, bytes, size); + buffer->position += size; +} + +static VALUE method_serialize(VALUE self, VALUE doc) { + char data[5] = {0x05, 0x00, 0x00, 0x00, 0x00}; + return rb_str_new(data, 5); +} + +void Init_cbson() { + VALUE CBson = rb_define_module("CBson"); + rb_define_module_function(CBson, "serialize", method_serialize, 1); +} diff --git a/ext/cbson/extconf.rb b/ext/cbson/extconf.rb new file mode 100644 index 0000000..29f8fac --- /dev/null +++ b/ext/cbson/extconf.rb @@ -0,0 +1,7 @@ +require 'mkmf' + +extension_name = 'cbson' + +dir_config(extension_name) + +create_makefile(extension_name) diff --git a/lib/mongo/util/bson.rb b/lib/mongo/util/bson.rb index c0e8af9..0ea4c79 100644 --- a/lib/mongo/util/bson.rb +++ b/lib/mongo/util/bson.rb @@ -72,21 +72,28 @@ class BSON @buf.to_a end - def serialize(obj) - raise "Document is null" unless obj + begin + require 'mongo/ext/cbson' + def serialize(obj) + @buf = ByteBuffer.new(CBson.serialize(obj)) + end + rescue LoadError + def serialize(obj) + raise "Document is null" unless obj - @buf.rewind - # put in a placeholder for the total size - @buf.put_int(0) + @buf.rewind + # put in a placeholder for the total size + @buf.put_int(0) - # Write key/value pairs. Always write _id first if it exists. - oid = obj['_id'] || obj[:_id] - serialize_key_value('_id', oid) if oid - obj.each {|k, v| serialize_key_value(k, v) unless k == '_id' || k == :_id } + # Write key/value pairs. Always write _id first if it exists. + oid = obj['_id'] || obj[:_id] + serialize_key_value('_id', oid) if oid + obj.each {|k, v| serialize_key_value(k, v) unless k == '_id' || k == :_id } - serialize_eoo_element(@buf) - @buf.put_int(@buf.size, 0) - self + serialize_eoo_element(@buf) + @buf.put_int(@buf.size, 0) + self + end end def serialize_key_value(k, v)