use correct ObjectID generation - no change to ordering yet though

This commit is contained in:
Mike Dirolf 2009-08-25 10:30:14 -04:00
parent 496af2be9c
commit e05c9fc5da
2 changed files with 35 additions and 56 deletions

View File

@ -15,6 +15,8 @@
# ++ # ++
require 'mutex_m' require 'mutex_m'
require 'socket'
require 'digest/md5'
require 'mongo/util/byte_buffer' require 'mongo/util/byte_buffer'
module Mongo module Mongo
@ -41,10 +43,6 @@ module Mongo
# 10 # 10
# 11 # 11
class ObjectID 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 # The string representation of an OID is different than its internal
# and BSON byte representations. The BYTE_ORDER here maps # and BSON byte representations. The BYTE_ORDER here maps
# internal/BSON byte position (the index in BYTE_ORDER) to the # internal/BSON byte position (the index in BYTE_ORDER) to the
@ -56,7 +54,6 @@ module Mongo
LOCK = Object.new LOCK = Object.new
LOCK.extend Mutex_m LOCK.extend Mutex_m
@@index_time = Time.new.to_i
@@index = 0 @@index = 0
# Given a string representation of an ObjectID, return a new ObjectID # Given a string representation of an ObjectID, return a new ObjectID
@ -78,9 +75,8 @@ module Mongo
end end
# +data+ is an array of bytes. If nil, a new id will be generated. # +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)
def initialize(data=nil, t=nil) @data = data || generate
@data = data || generate_id(t)
end end
def eql?(other) def eql?(other)
@ -100,34 +96,39 @@ module Mongo
str str
end end
# (Would normally be private, but isn't so we can test it.) private
def generate_id(t=nil)
t ||= Time.new.to_i def generate
# 4 bytes current time
time = Time.new.to_i
buf = ByteBuffer.new buf = ByteBuffer.new
buf.put_int(t & 0xffffffff) buf.put_int(time & 0xFFFFFFFF)
buf.put_array(MACHINE)
buf.put_array(PID) # 3 bytes machine
i = index_for_time(t) machine_hash = Digest::MD5.digest(Socket.gethostname)
buf.put(i & 0xff) buf.put(machine_hash[0])
buf.put((i >> 8) & 0xff) buf.put(machine_hash[1])
buf.put((i >> 16) & 0xff) 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.rewind
buf.to_a.dup buf.to_a.dup
end end
# (Would normally be private, but isn't so we can test it.) def get_inc
def index_for_time(t)
LOCK.mu_synchronize { LOCK.mu_synchronize {
if t != @@index_time @@index = (@@index + 1) % 0xFFFFFF
@@index = 0
@@index_time = t
end
retval = @@index
@@index += 1
retval
} }
end end
end end
end end

View File

@ -7,41 +7,19 @@ class ObjectIDTest < Test::Unit::TestCase
include Mongo include Mongo
def setup def setup
@t = 42 @o = ObjectID.new()
@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
end end
def test_different def test_different
o2 = ObjectID.new(nil, @t) a = ObjectID.new
assert @o.to_a != o2.to_a b = ObjectID.new
assert_not_equal a.to_a, b.to_a
assert_not_equal a, b
end end
def test_eql? def test_eql?
o2 = ObjectID.new(@o.to_a) o2 = ObjectID.new(@o.to_a)
assert @o.eql?(o2) assert_equal @o, o2
assert @o == o2
end end
def test_to_s def test_to_s