From b034daa391c7bf67ed96d2eb82393ef3f5586242 Mon Sep 17 00:00:00 2001 From: mbesancon Date: Sun, 19 Aug 2018 11:51:01 +0200 Subject: [PATCH 1/5] uuid5 function --- stdlib/UUIDs/docs/src/index.md | 1 + stdlib/UUIDs/src/UUIDs.jl | 32 +++++++++++++++++++++++++++++++- stdlib/UUIDs/test/runtests.jl | 4 ++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/stdlib/UUIDs/docs/src/index.md b/stdlib/UUIDs/docs/src/index.md index 3b936e7c82178..4c2f6a5e33c58 100644 --- a/stdlib/UUIDs/docs/src/index.md +++ b/stdlib/UUIDs/docs/src/index.md @@ -7,6 +7,7 @@ DocTestSetup = :(using UUIDs, Random) ```@docs UUIDs.uuid1 UUIDs.uuid4 +UUIDs.uuid5 UUIDs.uuid_version ``` diff --git a/stdlib/UUIDs/src/UUIDs.jl b/stdlib/UUIDs/src/UUIDs.jl index bf618cd6eeff3..913f77cac1cf4 100644 --- a/stdlib/UUIDs/src/UUIDs.jl +++ b/stdlib/UUIDs/src/UUIDs.jl @@ -4,7 +4,9 @@ module UUIDs using Random -export UUID, uuid1, uuid4, uuid_version +import SHA + +export UUID, uuid1, uuid4, uuid5, uuid_version import Base: UUID @@ -81,4 +83,32 @@ function uuid4(rng::AbstractRNG=Random.GLOBAL_RNG) UUID(u) end +""" + uuid5(ns::UUID, name::String) -> UUID + +Generates a version 5 (namespace and domain-based) universally unique identifier (UUID), +as specified by RFC 4122. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> u4 = uuid4(rng) +UUID("196f2941-2d58-45ba-9f13-43a2532b2fa8") + +julia> u5 = uuid5(u4, "julia") +``` +""" +function uuid5(ns::UUID, name::String) + #trucate SHA1 result to 16 bytes + hash_result = SHA.sha1(string(ns,name))[1:16] + # set version number to 5 + hash_result[6] = (hash_result[6] & 0xf) | (0x2) + v = zero(UInt128) + for idx in 1:16 + v = (v << 0x08) | hash_result[idx] + end + return UUID(v) +end + end diff --git a/stdlib/UUIDs/test/runtests.jl b/stdlib/UUIDs/test/runtests.jl index aa8a78307b0c8..cba5920dc7638 100644 --- a/stdlib/UUIDs/test/runtests.jl +++ b/stdlib/UUIDs/test/runtests.jl @@ -4,12 +4,16 @@ using Test, UUIDs, Random u1 = uuid1() u4 = uuid4() +u5 = uuid5(u1, "julia") @test uuid_version(u1) == 1 @test uuid_version(u4) == 4 +@test uuid_version(u5) == 5 @test u1 == UUID(string(u1)) == UUID(GenericString(string(u1))) @test u4 == UUID(string(u4)) == UUID(GenericString(string(u4))) +@test u5 == UUID(string(u5)) == UUID(GenericString(string(u5))) @test u1 == UUID(UInt128(u1)) @test u4 == UUID(UInt128(u4)) +@test u5 == UUID(UInt128(u5)) @test uuid4(MersenneTwister(0)) == uuid4(MersenneTwister(0)) @test_throws ArgumentError UUID("550e8400e29b-41d4-a716-446655440000") @test_throws ArgumentError UUID("550e8400e29b-41d4-a716-44665544000098") From e0f3f5181d3745ff12abb2edc8de644d7acc4630 Mon Sep 17 00:00:00 2001 From: mbesancon Date: Sun, 19 Aug 2018 12:17:16 +0200 Subject: [PATCH 2/5] set version number, remove hash slicing --- stdlib/UUIDs/src/UUIDs.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/stdlib/UUIDs/src/UUIDs.jl b/stdlib/UUIDs/src/UUIDs.jl index 913f77cac1cf4..a895b4c369b95 100644 --- a/stdlib/UUIDs/src/UUIDs.jl +++ b/stdlib/UUIDs/src/UUIDs.jl @@ -97,15 +97,17 @@ julia> u4 = uuid4(rng) UUID("196f2941-2d58-45ba-9f13-43a2532b2fa8") julia> u5 = uuid5(u4, "julia") +UUID("6f461186-52d8-5fc1-993a-77a729165b65") ``` """ function uuid5(ns::UUID, name::String) - #trucate SHA1 result to 16 bytes - hash_result = SHA.sha1(string(ns,name))[1:16] + hash_result = SHA.sha1(string(ns,name)) # set version number to 5 - hash_result[6] = (hash_result[6] & 0xf) | (0x2) + hash_result[7] = (hash_result[7] & 0x0F) | (0x50) + hash_result[9] = (hash_result[9] & 0x3F) | (0x80) v = zero(UInt128) - for idx in 1:16 + #use only the first 16 bytes of the SHA1 hash + for idx in Base.OneTo(16) v = (v << 0x08) | hash_result[idx] end return UUID(v) From a98b14a01b4396791567ec5564d77834611198a0 Mon Sep 17 00:00:00 2001 From: mbesancon Date: Mon, 20 Aug 2018 14:10:01 +0200 Subject: [PATCH 3/5] added tests for Python UUID reproducibility --- stdlib/UUIDs/src/UUIDs.jl | 15 ++++++++++++++- stdlib/UUIDs/test/runtests.jl | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/stdlib/UUIDs/src/UUIDs.jl b/stdlib/UUIDs/src/UUIDs.jl index a895b4c369b95..8b92296473c59 100644 --- a/stdlib/UUIDs/src/UUIDs.jl +++ b/stdlib/UUIDs/src/UUIDs.jl @@ -24,6 +24,13 @@ julia> uuid_version(uuid4()) """ uuid_version(u::UUID) = Int((u.value >> 76) & 0xf) +# Some UUID namespaces provided in the appendix of RFC 4122 +# https://tools.ietf.org/html/rfc4122.html#appendix-C +const namespace_dns = UUID(0x6ba7b8109dad11d180b400c04fd430c8) # 6ba7b810-9dad-11d1-80b4-00c04fd430c8 +const namespace_url = UUID(0x6ba7b8119dad11d180b400c04fd430c8) # 6ba7b811-9dad-11d1-80b4-00c04fd430c8 +const namespace_oid = UUID(0x6ba7b8129dad11d180b400c04fd430c8) # 6ba7b812-9dad-11d1-80b4-00c04fd430c8 +const namespace_x500 = UUID(0x6ba7b8149dad11d180b400c04fd430c8) # 6ba7b814-9dad-11d1-80b4-00c04fd430c8 + """ uuid1([rng::AbstractRNG=GLOBAL_RNG]) -> UUID @@ -101,7 +108,13 @@ UUID("6f461186-52d8-5fc1-993a-77a729165b65") ``` """ function uuid5(ns::UUID, name::String) - hash_result = SHA.sha1(string(ns,name)) + nsbytes = zeros(UInt8, 16) + nsv = ns.value + for idx in Base.OneTo(16) + nsbytes[idx] = nsv >> 120 + nsv = nsv << 8 + end + hash_result = SHA.sha1(append!(nsbytes, convert(Vector{UInt8}, codeunits(unescape_string(name))))) # set version number to 5 hash_result[7] = (hash_result[7] & 0x0F) | (0x50) hash_result[9] = (hash_result[9] & 0x3F) | (0x80) diff --git a/stdlib/UUIDs/test/runtests.jl b/stdlib/UUIDs/test/runtests.jl index cba5920dc7638..8973e6deb0632 100644 --- a/stdlib/UUIDs/test/runtests.jl +++ b/stdlib/UUIDs/test/runtests.jl @@ -18,3 +18,26 @@ u5 = uuid5(u1, "julia") @test_throws ArgumentError UUID("550e8400e29b-41d4-a716-446655440000") @test_throws ArgumentError UUID("550e8400e29b-41d4-a716-44665544000098") @test_throws ArgumentError UUID("z50e8400-e29b-41d4-a716-446655440000") + +# results similar to Python builtin uuid +# To reproduce the sequence +#= +import uuid +uuids = [uuid.UUID("22b4a8a1-e548-4eeb-9270-60426d66a48e")] +for _ in range(5): + uuids.append(uuid.uuid5(uuids[-1], "julia")) +=# + +const following_uuids = [ + UUID("22b4a8a1-e548-4eeb-9270-60426d66a48e"), + UUID("30ea6cfd-c270-569f-b4cb-795dead63686"), + UUID("31099374-e3a0-5fde-9482-791c639bf29b"), + UUID("6b34b357-a348-53aa-8c71-fb9b06c3a51e"), + UUID("fdbd7d4d-c462-59cc-ae6a-0c3b010240e2"), + UUID("d8cc6298-75d5-57e0-996c-279259ab365c"), +] + +for (idx, init_uuid) in enumerate(following_uuids[1:end-1]) + next_id = uuid5(init_uuid, "julia") + @test next_id == following_uuids[idx+1] +end From 028e18bcaefcab03f4937158611805dbe51d12d3 Mon Sep 17 00:00:00 2001 From: mbesancon Date: Mon, 20 Aug 2018 15:11:28 +0200 Subject: [PATCH 4/5] added DNS tests --- stdlib/UUIDs/test/runtests.jl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/stdlib/UUIDs/test/runtests.jl b/stdlib/UUIDs/test/runtests.jl index 8973e6deb0632..8258ac3f27d68 100644 --- a/stdlib/UUIDs/test/runtests.jl +++ b/stdlib/UUIDs/test/runtests.jl @@ -41,3 +41,16 @@ for (idx, init_uuid) in enumerate(following_uuids[1:end-1]) next_id = uuid5(init_uuid, "julia") @test next_id == following_uuids[idx+1] end + +# Python-generated UUID following each of the standard namespaces +const standard_namespace_uuids = [ + (UUIDs.namespace_dns, UUID("00ca23ad-40ef-500c-a910-157de3950d07")), + (UUIDs.namespace_oid, UUID("b7bf72b0-fb4e-538b-952a-3be296f07f6d")), + (UUIDs.namespace_url, UUID("997cd5be-4705-5439-9fe6-d77b18d612e5")), + (UUIDs.namespace_x500, UUID("993c6684-82e7-5cdb-bd46-9bff0362e6a9")), +] + +for (init_uuid, next_uuid) in standard_namespace_uuids + result = uuid5(init_uuid, "julia") + @test next_uuid == result +end From f8102bb81d16e1ef3214b5cb183b6dd79e6ab840 Mon Sep 17 00:00:00 2001 From: mbesancon Date: Mon, 20 Aug 2018 17:26:26 +0200 Subject: [PATCH 5/5] corrected docstring --- stdlib/UUIDs/src/UUIDs.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/UUIDs/src/UUIDs.jl b/stdlib/UUIDs/src/UUIDs.jl index 8b92296473c59..6a5e0adb79102 100644 --- a/stdlib/UUIDs/src/UUIDs.jl +++ b/stdlib/UUIDs/src/UUIDs.jl @@ -104,7 +104,7 @@ julia> u4 = uuid4(rng) UUID("196f2941-2d58-45ba-9f13-43a2532b2fa8") julia> u5 = uuid5(u4, "julia") -UUID("6f461186-52d8-5fc1-993a-77a729165b65") +UUID("b37756f8-b0c0-54cd-a466-19b3d25683bc") ``` """ function uuid5(ns::UUID, name::String)