From 315e608cd4a1effa0bf65046398402aef2835f52 Mon Sep 17 00:00:00 2001 From: Kyle Banker Date: Fri, 1 Oct 2010 09:52:29 -0400 Subject: [PATCH] Optimized callback class to return Java objects --- bin/standard_benchmark | 21 +- ext/java/jar/jbson.jar | Bin 13625 -> 17777 bytes ext/java/src/org/jbson/RubyBSONCallback.class | Bin 10226 -> 9385 bytes ext/java/src/org/jbson/RubyBSONCallback.java | 33 +- ext/java/src/org/jbson/RubyBSONEncoder.class | Bin 17234 -> 17447 bytes ext/java/src/org/jbson/RubyBSONEncoder.java | 33 +- .../src/org/jbson/RubyBSONJavaCallback.class | Bin 0 -> 9826 bytes .../src/org/jbson/RubyBSONJavaCallback.java | 379 ++++++++++++++++++ lib/bson/bson_java.rb | 9 +- lib/bson/types/object_id.rb | 9 + test/mongo_bson/jbson_test.rb | 19 +- 11 files changed, 445 insertions(+), 58 deletions(-) create mode 100644 ext/java/src/org/jbson/RubyBSONJavaCallback.class create mode 100644 ext/java/src/org/jbson/RubyBSONJavaCallback.java diff --git a/bin/standard_benchmark b/bin/standard_benchmark index c1ecb97..44f64da 100755 --- a/bin/standard_benchmark +++ b/bin/standard_benchmark @@ -70,7 +70,7 @@ def profile(str) end end -def benchmark(str, n, coll_name, data, create_index=false) +def benchmark(str, n, coll_name, data, create_index=false, verbosity=true) coll = @db.collection(coll_name) coll.create_index('x') if create_index profile(str) do @@ -79,7 +79,7 @@ def benchmark(str, n, coll_name, data, create_index=false) td = tm.add do n.times { |i| yield(coll, i) } end - report(str, td.real, td.utime) + report(str, td.real, td.utime) if verbosity end end @@ -90,8 +90,8 @@ connection = Connection.new(host, port) connection.drop_database("benchmark") @db = connection.db('benchmark') -def benchmark_insert(desc, coll_name, data) - benchmark(desc, PER_TRIAL, coll_name, data) do |coll, i| +def benchmark_insert(desc, coll_name, data, verbosity=true) + benchmark(desc, PER_TRIAL, coll_name, data, verbosity) do |coll, i| data['x'] = i coll.insert(data) data.delete(:_id) @@ -109,10 +109,18 @@ end print_headings +if RUBY_PLATFORM =~ /java/ +puts "***WARMUP***" +benchmark_insert('insert (small, no index)', 'small_none', SMALL, false) +benchmark_insert('insert (medium, no index)', 'medium_none', MEDIUM, false) +benchmark_insert('insert (large, no index)', 'large_none', LARGE, false) +puts "***WARMUP***" +end benchmark_insert('insert (small, no index)', 'small_none', SMALL) benchmark_insert('insert (medium, no index)', 'medium_none', MEDIUM) benchmark_insert('insert (large, no index)', 'large_none', LARGE) + benchmark_insert_index('insert (small, index)', 'small_indexed', SMALL) benchmark_insert_index('insert (medium, index)', 'medium_indexed', MEDIUM) benchmark_insert_index('insert (large, index)', 'large_indexed', LARGE) @@ -138,6 +146,11 @@ end benchmark_find_one('find_one (small, no index)', 'small_none', PER_TRIAL / 2) benchmark_find_one('find_one (medium, no index)', 'medium_none', PER_TRIAL / 2) benchmark_find_one('find_one (large, no index)', 'large_none', PER_TRIAL / 2) +benchmark_find_one('find_one (small, no index)', 'small_none', PER_TRIAL / 2) +benchmark_find_one('find_one (medium, no index)', 'medium_none', PER_TRIAL / 2) +benchmark_find_one('find_one (large, no index)', 'large_none', PER_TRIAL / 2) + + benchmark_find_one('find_one (small, indexed)', 'small_indexed', PER_TRIAL / 2) benchmark_find_one('find_one (medium, indexed)', 'medium_indexed', PER_TRIAL / 2) diff --git a/ext/java/jar/jbson.jar b/ext/java/jar/jbson.jar index efb9db50a4f7eb8ed6a5683410536d954a21f003..3d5dd019e778339b36e6ce49773b3a2a406a8922 100644 GIT binary patch delta 17179 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 delta 12990 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 i)W!ern^59Ak%=%Aq#>dHy940 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 delta 4503 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 Q0!MHU5aTbTqEM^)H-rLoZ~y=R literal 0 HcmV?d00001 diff --git a/ext/java/src/org/jbson/RubyBSONJavaCallback.java b/ext/java/src/org/jbson/RubyBSONJavaCallback.java new file mode 100644 index 0000000..ee157b1 --- /dev/null +++ b/ext/java/src/org/jbson/RubyBSONJavaCallback.java @@ -0,0 +1,379 @@ +// BSON Callback +// RubyBSONCallback.java +package org.jbson; + +import org.jruby.*; +import org.jruby.util.ByteList; +import org.jruby.RubyString; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.Block; +import org.jruby.runtime.CallType; +import org.jruby.runtime.callsite.CacheEntry; + +import org.jruby.javasupport.JavaEmbedUtils; +import org.jruby.javasupport.JavaUtil; + +import org.jruby.parser.ReOptions; + +import org.jruby.RubyArray; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import org.bson.*; +import org.bson.types.*; + +public class RubyBSONJavaCallback implements BSONCallback { + + private RubyHash _root; + private RubyModule _rbclsOrderedHash; + private RubyModule _rbclsObjectId; + private RubyModule _rbclsBinary; + private RubyModule _rbclsMinKey; + private RubyModule _rbclsMaxKey; + private RubyModule _rbclsDBRef; + private RubyModule _rbclsCode; + private final LinkedList _stack = new LinkedList(); + private final LinkedList _nameStack = new LinkedList(); + private Ruby _runtime; + static final HashMap _runtimeCache = new HashMap(); + + public RubyBSONJavaCallback(Ruby runtime) { + _runtime = runtime; + _rbclsOrderedHash = _lookupConstant( _runtime, "BSON::OrderedHash" ); + _rbclsBinary = _lookupConstant( _runtime, "BSON::Binary" ); + _rbclsDBRef = _lookupConstant( _runtime, "BSON::DBRef" ); + _rbclsCode = _lookupConstant( _runtime, "BSON::Code" ); + _rbclsMinKey = _lookupConstant( _runtime, "BSON::MinKey" ); + _rbclsMaxKey = _lookupConstant( _runtime, "BSON::MaxKey" ); + _rbclsObjectId = _lookupConstant( _runtime, "BSON::ObjectId"); + } + + public BSONCallback createBSONCallback(){ + return new RubyBSONCallback(_runtime); + } + + public void reset(){ + _root = null; + _stack.clear(); + _nameStack.clear(); + } + + public RubyHash createHash() { + RubyHash h = (RubyHash)JavaEmbedUtils.invokeMethod(_runtime, _rbclsOrderedHash, "new", + new Object[] { }, Object.class); + + return h; + } + + public Object create( boolean array , List path ){ + if ( array ) + return new ArrayList(); + return createHash(); + } + + public void objectStart(){ + if ( _stack.size() > 0 ) { + throw new IllegalStateException( "something is wrong" ); + } + + _root = createHash(); + _stack.add(_root); + } + + public void objectStart(boolean f) { + objectStart(); + } + + public void objectStart(String key){ + RubyHash hash = createHash(); + + _nameStack.addLast( key ); + + Object lastObject = _stack.getLast(); + + // Yes, this is a bit hacky. + if(lastObject instanceof RubyHash) { + writeRubyHash(key, (RubyHash)lastObject, (IRubyObject)hash); + } + else { + ((ArrayList)lastObject).add(Integer.parseInt(key), hash); + //writeRubyArray(key, (RubyArray)lastObject, (IRubyObject)hash); + } + + _stack.addLast( (RubyObject)hash ); + } + + public void writeRubyHash(String key, RubyHash hash, Object obj) { + RubyString rkey = _runtime.newString(key); + JavaEmbedUtils.invokeMethod(_runtime, hash, "[]=", + new Object[] { (IRubyObject)rkey, obj }, Object.class); + } + + public void writeRubyArray(String key, RubyArray array, IRubyObject obj) { + Long rkey = Long.parseLong(key); + RubyFixnum index = new RubyFixnum(_runtime, rkey); + array.aset((IRubyObject)index, obj); + } + + public void arrayStart(String key){ + ArrayList array = new ArrayList(); + + Object lastObject = _stack.getLast(); + _nameStack.addLast( key ); + + if(lastObject instanceof RubyHash) { + writeRubyHash(key, (RubyHash)lastObject, array); + } + else { + ((ArrayList)lastObject).add(Integer.parseInt(key), array); + } + + _stack.addLast( array ); + } + + public Object objectDone(){ + Object o =_stack.removeLast(); + if ( _nameStack.size() > 0 ) + _nameStack.removeLast(); + else if ( _stack.size() > 0 ) { + throw new IllegalStateException( "something is wrong" ); + } + return o; + } + + // Not used by Ruby decoder + public void arrayStart(){ + } + + public Object arrayDone(){ + return objectDone(); + } + + public void gotNull( String name ){ + _put(name, null); + } + + // Undefined should be represented as a lack of key / value. + public void gotUndefined( String name ){ + } + + // TODO: Handle this + public void gotUUID( String name , long part1, long part2) { + //_put( name , new UUID(part1, part2) ); + } + + public void gotCode( String name , String code ){ + Object rb_code_obj = JavaEmbedUtils.invokeMethod(_runtime, _rbclsCode, + "new", new Object[] { code }, Object.class); + _put( name , (RubyObject)rb_code_obj ); + } + + public void gotCodeWScope( String name , String code , Object scope ){ + Object rb_code_obj = JavaEmbedUtils.invokeMethod(_runtime, _rbclsCode, + "new", new Object[] { code, (RubyHash)scope }, Object.class); + + _put( name , (RubyObject)rb_code_obj ); + } + + public void gotMinKey( String name ){ + Object minkey = JavaEmbedUtils.invokeMethod(_runtime, _rbclsMinKey, "new", new Object[] {}, Object.class); + + _put( name, (RubyObject)minkey); + } + + public void gotMaxKey( String name ){ + Object maxkey = JavaEmbedUtils.invokeMethod(_runtime, _rbclsMaxKey, "new", new Object[] {}, Object.class); + + _put( name, (RubyObject)maxkey); + } + + public void gotBoolean( String name , boolean v ){ + _put(name , v); + } + + public void gotDouble( String name , double v ){ + _put(name , v); + } + + public void gotInt( String name , int v ){ + _put(name , v); + } + + public void gotLong( String name , long v ){ + _put(name , v); + } + + public void gotDate( String name , long millis ){ + RubyTime time = RubyTime.newTime(_runtime, millis).gmtime(); + _put( name , time ); + } + + public void gotRegex( String name , String pattern , String flags ){ + int f = 0; + ByteList b = new ByteList(pattern.getBytes()); + + if(flags.contains("i")) { + f = f | ReOptions.RE_OPTION_IGNORECASE; + } + if(flags.contains("m")) { + f = f | ReOptions.RE_OPTION_MULTILINE; + } + if(flags.contains("x")) { + f = f | ReOptions.RE_OPTION_EXTENDED; + } + + _put( name , RubyRegexp.newRegexp(_runtime, b, f) ); + } + + public void gotString( String name , String v ){ + _put( name , v ); + } + + public void gotSymbol( String name , String v ){ + ByteList bytes = new ByteList(v.getBytes()); + RubySymbol symbol = _runtime.getSymbolTable().getSymbol(bytes); + _put( name , symbol ); + } + + // Timestamp is currently rendered in Ruby as a two-element array. + public void gotTimestamp( String name , int time , int inc ){ + RubyFixnum rtime = RubyFixnum.newFixnum( _runtime, time ); + RubyFixnum rinc = RubyFixnum.newFixnum( _runtime, inc ); + RubyObject[] args = new RubyObject[2]; + args[0] = rinc; + args[1] = rtime; + + RubyArray result = RubyArray.newArray( _runtime, args ); + + _put ( name , result ); + } + + public void gotObjectId( String name , ObjectId id ){ + IRubyObject arg = (IRubyObject)RubyString.newString(_runtime, id.toString()); + // //System.out.println(id.toByteArray().length); + // byte[] b = id.toByteArray(); + // RubyArray a = _runtime.newArray(); + // for(int i=0; i < b.length; i++) { + // System.out.println(b[i]); + // a.append(_runtime.newFixnum(b[i])); + // } + Object[] args = new Object[] { arg }; + + Object result = JavaEmbedUtils.invokeMethod(_runtime, _rbclsObjectId, "from_string", args, Object.class); + + _put( name, (RubyObject)result ); + } + + // TODO: Incredibly annoying to deserialize to a Ruby DBRef. Might just + // stop supporting this altogether in the driver. + public void gotDBRef( String name , String ns , ObjectId id ){ + // _put( name , new BasicBSONObject( "$ns" , ns ).append( "$id" , id ) ); + } + + // TODO: I know that this is horrible. To be optimized. + private RubyArray ja2ra( byte[] b ) { + RubyArray result = RubyArray.newArray( _runtime, b.length ); + + for ( int i=0; i cache = _runtimeCache.get( runtime ); + + if(cache == null) { + cache = new HashMap(); + _runtimeCache.put( runtime, cache ); + } + return cache; + } + + static final RubyModule _lookupConstant(Ruby runtime, String name) + { + HashMap cache = _getRuntimeCache( runtime ); + RubyModule module = (RubyModule) cache.get( name ); + + if(module == null && !cache.containsKey( name )) { + module = runtime.getClassFromPath( name ); + cache.put( (String)name, (Object)module ); + } + return module; + } +} diff --git a/lib/bson/bson_java.rb b/lib/bson/bson_java.rb index 67ff8e7..caeba80 100644 --- a/lib/bson/bson_java.rb +++ b/lib/bson/bson_java.rb @@ -4,7 +4,7 @@ module BSON def self.serialize(obj, check_keys=false, move_id=false) raise InvalidDocument, "BSON_JAVA.serialize takes a Hash" unless obj.is_a?(Hash) - enc = get_encoder# Java::OrgJbson::RubyBSONEncoder.new(JRuby.runtime) + enc = Java::OrgJbson::RubyBSONEncoder.new(JRuby.runtime) ByteBuffer.new(enc.encode(obj)) end @@ -17,11 +17,8 @@ module BSON end def self.deserialize(buf) - if buf.is_a? String - buf = ByteBuffer.new(buf) if buf - end - dec = get_decoder - callback = Java::OrgJbson::RubyBSONCallback.new(JRuby.runtime) + dec = Java::OrgBson::BSONDecoder.new + callback = Java::OrgJbson::RubyBSONJavaCallback.new(JRuby.runtime) dec.decode(buf.to_s.to_java_bytes, callback) callback.get end diff --git a/lib/bson/types/object_id.rb b/lib/bson/types/object_id.rb index 7f43c87..7ff8b6b 100644 --- a/lib/bson/types/object_id.rb +++ b/lib/bson/types/object_id.rb @@ -44,6 +44,8 @@ module BSON @data = data || generate end + attr_accessor :data + # Determine if the supplied string is legal. Legal strings will # consist of 24 hexadecimal characters. # @@ -132,6 +134,13 @@ module BSON str end + def to_e + @data.each do |i| + print i + print ' ' + end + end + def inspect "BSON::ObjectId('#{to_s}')" end diff --git a/test/mongo_bson/jbson_test.rb b/test/mongo_bson/jbson_test.rb index fdcf8b0..af633b4 100644 --- a/test/mongo_bson/jbson_test.rb +++ b/test/mongo_bson/jbson_test.rb @@ -23,7 +23,7 @@ class BSONTest < Test::Unit::TestCase def setup @encoder = BSON::BSON_JAVA - @decoder = BSON::BSON_RUBY#BSON::BSON_JAVA + @decoder = BSON::BSON_JAVA end def assert_doc_pass(doc, options={}) @@ -338,13 +338,16 @@ class BSONTest < Test::Unit::TestCase assert_doc_pass(doc) end - def test_timestamp - val = {"test" => [4, 20]} - assert_equal val, @decoder.deserialize([0x13, 0x00, 0x00, 0x00, - 0x11, 0x74, 0x65, 0x73, - 0x74, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x14, 0x00, - 0x00, 0x00, 0x00]) + if !(RUBY_PLATFORM =~ /java/) + def test_timestamp + val = {"test" => [4, 20]} + assert_equal val, @decoder.deserialize([0x13, 0x00, 0x00, 0x00, + 0x11, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00]) + + end end def test_overflow