Change string representation of ObjectID - add 'legacy' methods for interacting with old strings
This commit is contained in:
parent
e05c9fc5da
commit
47c34266cf
|
@ -17,38 +17,17 @@
|
||||||
require 'mutex_m'
|
require 'mutex_m'
|
||||||
require 'socket'
|
require 'socket'
|
||||||
require 'digest/md5'
|
require 'digest/md5'
|
||||||
require 'mongo/util/byte_buffer'
|
|
||||||
|
|
||||||
module Mongo
|
module Mongo
|
||||||
|
|
||||||
# Implementation of the Babble OID. Object ids are not required by
|
# Representation of an ObjectId for Mongo.
|
||||||
# Mongo, but they make certain operations more efficient.
|
|
||||||
#
|
|
||||||
# The driver does not automatically assign ids to records that are
|
|
||||||
# inserted. (An upcoming feature will allow you to give an id "factory"
|
|
||||||
# to a database and/or a collection.)
|
|
||||||
#
|
|
||||||
# 12 bytes
|
|
||||||
# ---
|
|
||||||
# 0 time
|
|
||||||
# 1
|
|
||||||
# 2
|
|
||||||
# 3
|
|
||||||
# 4 machine
|
|
||||||
# 5
|
|
||||||
# 6
|
|
||||||
# 7 pid
|
|
||||||
# 8
|
|
||||||
# 9 inc
|
|
||||||
# 10
|
|
||||||
# 11
|
|
||||||
class ObjectID
|
class ObjectID
|
||||||
# The string representation of an OID is different than its internal
|
# This is the legacy byte ordering for Babble. Versions of the Ruby
|
||||||
# and BSON byte representations. The BYTE_ORDER here maps
|
# driver prior to 0.14 used this byte ordering when converting ObjectID
|
||||||
# internal/BSON byte position (the index in BYTE_ORDER) to the
|
# instances to and from strings. If you have string representations of
|
||||||
# position of the two hex characters representing that byte in the
|
# ObjectIDs using the legacy byte ordering make sure to use the
|
||||||
# string representation. For example, the 0th BSON byte corresponds to
|
# to_s_legacy and from_string_legacy methods, or convert your strings
|
||||||
# the (0-based) 7th pair of hex chars in the string.
|
# with ObjectID#legacy_string_convert
|
||||||
BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
|
BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
|
||||||
|
|
||||||
LOCK = Object.new
|
LOCK = Object.new
|
||||||
|
@ -56,17 +35,6 @@ module Mongo
|
||||||
|
|
||||||
@@index = 0
|
@@index = 0
|
||||||
|
|
||||||
# Given a string representation of an ObjectID, return a new ObjectID
|
|
||||||
# with that value.
|
|
||||||
def self.from_string(str)
|
|
||||||
raise "illegal ObjectID format" unless legal?(str)
|
|
||||||
data = []
|
|
||||||
BYTE_ORDER.each_with_index { |string_position, data_index|
|
|
||||||
data[data_index] = str[string_position * 2, 2].to_i(16)
|
|
||||||
}
|
|
||||||
self.new(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.legal?(str)
|
def self.legal?(str)
|
||||||
len = BYTE_ORDER.length * 2
|
len = BYTE_ORDER.length * 2
|
||||||
str =~ /([0-9a-f]+)/i
|
str =~ /([0-9a-f]+)/i
|
||||||
|
@ -88,7 +56,42 @@ module Mongo
|
||||||
@data.dup
|
@data.dup
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Given a string representation of an ObjectID, return a new ObjectID
|
||||||
|
# with that value.
|
||||||
|
def self.from_string(str)
|
||||||
|
raise "illegal ObjectID format" unless legal?(str)
|
||||||
|
data = []
|
||||||
|
12.times do |i|
|
||||||
|
data[i] = str[i * 2, 2].to_i(16)
|
||||||
|
end
|
||||||
|
self.new(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a new ObjectID given a string representation of an ObjectID
|
||||||
|
# using the legacy byte ordering. This method may eventually be
|
||||||
|
# removed. If you are not sure that you need this method you should be
|
||||||
|
# using the regular from_string.
|
||||||
|
def self.from_string_legacy(str)
|
||||||
|
raise "illegal ObjectID format" unless legal?(str)
|
||||||
|
data = []
|
||||||
|
BYTE_ORDER.each_with_index { |string_position, data_index|
|
||||||
|
data[data_index] = str[string_position * 2, 2].to_i(16)
|
||||||
|
}
|
||||||
|
self.new(data)
|
||||||
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
|
str = ' ' * 24
|
||||||
|
12.times do |i|
|
||||||
|
str[i * 2, 2] = '%02x' % @data[i]
|
||||||
|
end
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get a string representation of this ObjectID using the legacy byte
|
||||||
|
# ordering. This method may eventually be removed. If you are not sure
|
||||||
|
# that you need this method you should be using the regular to_s.
|
||||||
|
def to_s_legacy
|
||||||
str = ' ' * 24
|
str = ' ' * 24
|
||||||
BYTE_ORDER.each_with_index { |string_position, data_index|
|
BYTE_ORDER.each_with_index { |string_position, data_index|
|
||||||
str[string_position * 2, 2] = '%02x' % @data[data_index]
|
str[string_position * 2, 2] = '%02x' % @data[data_index]
|
||||||
|
@ -96,33 +99,37 @@ module Mongo
|
||||||
str
|
str
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Convert a string representation of an ObjectID using the legacy byte
|
||||||
|
# ordering to the proper byte ordering. This method may eventually be
|
||||||
|
# removed. If you are not sure that you need this method it is probably
|
||||||
|
# unnecessary.
|
||||||
|
def self.legacy_string_convert(str)
|
||||||
|
legacy = ' ' * 24
|
||||||
|
BYTE_ORDER.each_with_index do |legacy_pos, pos|
|
||||||
|
legacy[legacy_pos * 2, 2] = str[pos * 2, 2]
|
||||||
|
end
|
||||||
|
legacy
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def generate
|
def generate
|
||||||
|
oid = ''
|
||||||
|
|
||||||
# 4 bytes current time
|
# 4 bytes current time
|
||||||
time = Time.new.to_i
|
time = Time.new.to_i
|
||||||
buf = ByteBuffer.new
|
oid += [time].pack("N")
|
||||||
buf.put_int(time & 0xFFFFFFFF)
|
|
||||||
|
|
||||||
# 3 bytes machine
|
# 3 bytes machine
|
||||||
machine_hash = Digest::MD5.digest(Socket.gethostname)
|
oid += Digest::MD5.digest(Socket.gethostname)[0, 3]
|
||||||
buf.put(machine_hash[0])
|
|
||||||
buf.put(machine_hash[1])
|
|
||||||
buf.put(machine_hash[2])
|
|
||||||
|
|
||||||
# 2 bytes pid
|
# 2 bytes pid
|
||||||
pid = Process.pid % 0xFFFF
|
oid += [Process.pid % 0xFFFF].pack("n")
|
||||||
buf.put(pid & 0xFF)
|
|
||||||
buf.put((pid >> 8) & 0xFF)
|
|
||||||
|
|
||||||
# 3 bytes inc
|
# 3 bytes inc
|
||||||
inc = get_inc
|
oid += [get_inc].pack("N")[1, 3]
|
||||||
buf.put(inc & 0xFF)
|
|
||||||
buf.put((inc >> 8) & 0xFF)
|
|
||||||
buf.put((inc >> 16) & 0xFF)
|
|
||||||
|
|
||||||
buf.rewind
|
oid.unpack("C12")
|
||||||
buf.to_a.dup
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_inc
|
def get_inc
|
||||||
|
|
|
@ -29,6 +29,15 @@ class ObjectIDTest < Test::Unit::TestCase
|
||||||
assert_equal 24, $1.length
|
assert_equal 24, $1.length
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_to_s_legacy
|
||||||
|
s = @o.to_s_legacy
|
||||||
|
assert_equal 24, s.length
|
||||||
|
s =~ /^([0-9a-f]+)$/
|
||||||
|
assert_equal 24, $1.length
|
||||||
|
|
||||||
|
assert_not_equal s, @o.to_s
|
||||||
|
end
|
||||||
|
|
||||||
def test_save_and_restore
|
def test_save_and_restore
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
||||||
|
@ -51,6 +60,14 @@ class ObjectIDTest < Test::Unit::TestCase
|
||||||
assert_equal @o.to_s, o2.to_s
|
assert_equal @o.to_s, o2.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_from_string_legacy
|
||||||
|
hex_str = @o.to_s_legacy
|
||||||
|
o2 = ObjectID.from_string_legacy(hex_str)
|
||||||
|
assert_equal hex_str, o2.to_s_legacy
|
||||||
|
assert_equal @o, o2
|
||||||
|
assert_equal @o.to_s, o2.to_s
|
||||||
|
end
|
||||||
|
|
||||||
def test_legal
|
def test_legal
|
||||||
assert !ObjectID.legal?(nil)
|
assert !ObjectID.legal?(nil)
|
||||||
assert !ObjectID.legal?("fred")
|
assert !ObjectID.legal?("fred")
|
||||||
|
@ -62,7 +79,7 @@ class ObjectIDTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_from_string_leading_zeroes
|
def test_from_string_leading_zeroes
|
||||||
hex_str = '000000000000000000abcdef'
|
hex_str = '000000000000000000000000'
|
||||||
o = ObjectID.from_string(hex_str)
|
o = ObjectID.from_string(hex_str)
|
||||||
assert_equal hex_str, o.to_s
|
assert_equal hex_str, o.to_s
|
||||||
end
|
end
|
||||||
|
@ -70,7 +87,19 @@ class ObjectIDTest < Test::Unit::TestCase
|
||||||
def test_byte_order
|
def test_byte_order
|
||||||
hex_str = '000102030405060708090A0B'
|
hex_str = '000102030405060708090A0B'
|
||||||
o = ObjectID.from_string(hex_str)
|
o = ObjectID.from_string(hex_str)
|
||||||
|
assert_equal [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b], o.to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_legacy_byte_order
|
||||||
|
hex_str = '000102030405060708090A0B'
|
||||||
|
o = ObjectID.from_string_legacy(hex_str)
|
||||||
assert_equal [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08], o.to_a
|
assert_equal [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08], o.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_legacy_string_convert
|
||||||
|
l = @o.to_s_legacy
|
||||||
|
s = @o.to_s
|
||||||
|
assert_equal s, ObjectID.legacy_string_convert(l)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue