diff --git a/lib/mongo/types/objectid.rb b/lib/mongo/types/objectid.rb index d4ebb61..ec46b8b 100644 --- a/lib/mongo/types/objectid.rb +++ b/lib/mongo/types/objectid.rb @@ -15,6 +15,8 @@ # ++ require 'mutex_m' +require 'socket' +require 'digest/md5' require 'mongo/util/byte_buffer' module Mongo @@ -41,10 +43,6 @@ module Mongo # 10 # 11 class ObjectID - - MACHINE = ( val = rand(0x1000000); [val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff] ) - PID = ( val = rand(0x10000); [val & 0xff, (val >> 8) & 0xff]; ) - # The string representation of an OID is different than its internal # and BSON byte representations. The BYTE_ORDER here maps # internal/BSON byte position (the index in BYTE_ORDER) to the @@ -56,7 +54,6 @@ module Mongo LOCK = Object.new LOCK.extend Mutex_m - @@index_time = Time.new.to_i @@index = 0 # Given a string representation of an ObjectID, return a new ObjectID @@ -78,9 +75,8 @@ module Mongo end # +data+ is an array of bytes. If nil, a new id will be generated. - # The time +t+ is only used for testing; leave it nil. - def initialize(data=nil, t=nil) - @data = data || generate_id(t) + def initialize(data=nil) + @data = data || generate end def eql?(other) @@ -100,34 +96,39 @@ module Mongo str end - # (Would normally be private, but isn't so we can test it.) - def generate_id(t=nil) - t ||= Time.new.to_i + private + + def generate + # 4 bytes current time + time = Time.new.to_i buf = ByteBuffer.new - buf.put_int(t & 0xffffffff) - buf.put_array(MACHINE) - buf.put_array(PID) - i = index_for_time(t) - buf.put(i & 0xff) - buf.put((i >> 8) & 0xff) - buf.put((i >> 16) & 0xff) + buf.put_int(time & 0xFFFFFFFF) + + # 3 bytes machine + machine_hash = Digest::MD5.digest(Socket.gethostname) + buf.put(machine_hash[0]) + buf.put(machine_hash[1]) + buf.put(machine_hash[2]) + + # 2 bytes pid + pid = Process.pid % 0xFFFF + buf.put(pid & 0xFF) + buf.put((pid >> 8) & 0xFF) + + # 3 bytes inc + inc = get_inc + buf.put(inc & 0xFF) + buf.put((inc >> 8) & 0xFF) + buf.put((inc >> 16) & 0xFF) buf.rewind buf.to_a.dup end - # (Would normally be private, but isn't so we can test it.) - def index_for_time(t) + def get_inc LOCK.mu_synchronize { - if t != @@index_time - @@index = 0 - @@index_time = t - end - retval = @@index - @@index += 1 - retval + @@index = (@@index + 1) % 0xFFFFFF } end - end end diff --git a/test/test_objectid.rb b/test/test_objectid.rb index 2daf08c..b41c8f5 100644 --- a/test/test_objectid.rb +++ b/test/test_objectid.rb @@ -7,41 +7,19 @@ class ObjectIDTest < Test::Unit::TestCase include Mongo def setup - @t = 42 - @o = ObjectID.new(nil, @t) - end - - def test_index_for_time - t = 99 - assert_equal 0, @o.index_for_time(t) - assert_equal 1, @o.index_for_time(t) - assert_equal 2, @o.index_for_time(t) - t = 100 - assert_equal 0, @o.index_for_time(t) - end - - def test_time_bytes - a = @o.to_a - assert_equal @t, a[0] - 3.times { |i| assert_equal 0, a[i+1] } - - t = 43 - o = ObjectID.new(nil, t) - a = o.to_a - assert_equal t, a[0] - 3.times { |i| assert_equal 0, a[i+1] } - assert_equal 1, o.index_for_time(t) # 0 was used for o + @o = ObjectID.new() end def test_different - o2 = ObjectID.new(nil, @t) - assert @o.to_a != o2.to_a + a = ObjectID.new + b = ObjectID.new + assert_not_equal a.to_a, b.to_a + assert_not_equal a, b end def test_eql? o2 = ObjectID.new(@o.to_a) - assert @o.eql?(o2) - assert @o == o2 + assert_equal @o, o2 end def test_to_s