@@ -13,6 +13,7 @@ defmodule UUID do
1313 @ uuid_v3 3 # UUID v3 identifier.
1414 @ uuid_v4 4 # UUID v4 identifier.
1515 @ uuid_v5 5 # UUID v5 identifier.
16+ @ uuid_v6 6 # UUID v6 identifier.
1617
1718 @ urn "urn:uuid:" # UUID URN prefix.
1819
@@ -450,6 +451,114 @@ defmodule UUID do
450451 "Invalid argument; Expected: :dns|:url|:oid|:x500|:nil OR String, String"
451452 end
452453
454+ @ doc """
455+ Generate a new UUID v6. This version uses a combination of one or more of:
456+ unix epoch, random bytes, pid hash, and hardware address.
457+
458+ Accepts a `node_type` argument that can be either `:mac_address` or
459+ `:random_bytes`. Defaults to `:mac_address`. However, if there is a security
460+ concern with using a MAC address, use `:random_bytes`.
461+
462+ See the [RFC draft, section 3.3](https://tools.ietf.org/html/draft-peabody-dispatch-new-uuid-format-00#section-3.3)
463+ for more information on the node parts.
464+
465+ ## Examples
466+
467+ iex> UUID.uuid6()
468+ "1eb0d28f-da4c-6eb2-adc1-0242ac120002"
469+
470+ iex> UUID.uuid6(:random_bytes, :default)
471+ "1eb0d297-eb1e-62a6-a37f-a55eda5dd6e4"
472+
473+ iex> UUID.uuid6(:random_bytes, :hex)
474+ "1eb0d298502563fcadcd25e5d0a44c1a"
475+
476+ iex> UUID.uuid6(:random_bytes, :urn)
477+ "urn:uuid:1eb0d298-ca10-6914-ab0e-7d7e1e6e1808"
478+
479+ iex> UUID.uuid6(:random_bytes, :raw)
480+ <<30, 176, 210, 153, 52, 23, 102, 230, 164, 146, 99, 66, 4, 72, 220, 114>>
481+
482+ iex> UUID.uuid6(:random_bytes, :slug)
483+ "HrDSmab8ZnqR4SKw4LN-UA"
484+
485+ """
486+ def uuid6 ( node_type \\ :mac_address , format \\ :default )
487+ when node_type in [ :mac_address , :random_bytes ] do
488+ uuid6 ( uuid1_clockseq ( ) , uuid6_node ( node_type ) , format )
489+ end
490+
491+ @ doc """
492+ Generate a new UUID v6, with an existing clock sequence and node address. This
493+ version uses a combination of one or more of: unix epoch, random bytes,
494+ pid hash, and hardware address.
495+ """
496+ def uuid6 ( << clock_seq :: 14 >> , << node :: 48 >> , format ) do
497+ << time_hi :: 12 , time_mid :: 16 , time_low :: 32 >> = uuid1_time ( )
498+ << time_low1 :: 20 , time_low2 :: 12 >> = << time_low :: 32 >>
499+ << clock_seq_hi :: 6 , clock_seq_low :: 8 >> = << clock_seq :: 14 >>
500+
501+ << time_hi :: 12 , time_mid :: 16 , time_low1 :: 20 , @ uuid_v6 :: 4 , time_low2 :: 12 ,
502+ @ variant10 :: 2 , clock_seq_hi :: 6 , clock_seq_low :: 8 , node :: 48 >>
503+ |> uuid_to_string ( format )
504+ end
505+ def uuid6 ( _ , _ , _ ) do
506+ raise ArgumentError , message:
507+ "Invalid argument; Expected: <<clock_seq::14>>, <<node::48>>"
508+ end
509+
510+ @ doc """
511+ Convert a UUID v1 to a UUID v6 in the same format.
512+
513+ ## Examples
514+
515+ iex> UUID.uuid1_to_uuid6("dafc431a-0d21-11eb-adc1-0242ac120002")
516+ "1eb0d21d-afc4-631a-adc1-0242ac120002"
517+
518+ iex> UUID.uuid1_to_uuid6("2vxDGg0hEeutwQJCrBIAAg")
519+ "HrDSHa_EYxqtwQJCrBIAAg"
520+
521+ iex> UUID.uuid1_to_uuid6(<<218, 252, 67, 26, 13, 33, 17, 235, 173, 193, 2, 66, 172, 18, 0, 2>>)
522+ <<30, 176, 210, 29, 175, 196, 99, 26, 173, 193, 2, 66, 172, 18, 0, 2>>
523+
524+ """
525+ def uuid1_to_uuid6 ( uuid1 ) do
526+ { format , ub1 } = uuid_string_to_hex_pair ( uuid1 )
527+
528+ << time_low :: 32 , time_mid :: 16 , @ uuid_v1 :: 4 , time_hi :: 12 , rest :: binary >> = ub1
529+ << time_low1 :: 20 , time_low2 :: 12 >> = << time_low :: 32 >>
530+
531+ << time_hi :: 12 , time_mid :: 16 , time_low1 :: 20 , @ uuid_v6 :: 4 , time_low2 :: 12 ,
532+ rest :: binary >>
533+ |> uuid_to_string ( format )
534+ end
535+
536+ @ doc """
537+ Convert a UUID v6 to a UUID v1 in the same format.
538+
539+ ## Examples
540+
541+ iex> UUID.uuid6_to_uuid1("1eb0d21d-afc4-631a-adc1-0242ac120002")
542+ "dafc431a-0d21-11eb-adc1-0242ac120002"
543+
544+ iex> UUID.uuid6_to_uuid1("HrDSHa_EYxqtwQJCrBIAAg")
545+ "2vxDGg0hEeutwQJCrBIAAg"
546+
547+ iex> UUID.uuid6_to_uuid1(<<30, 176, 210, 29, 175, 196, 99, 26, 173, 193, 2, 66, 172, 18, 0, 2>>)
548+ <<218, 252, 67, 26, 13, 33, 17, 235, 173, 193, 2, 66, 172, 18, 0, 2>>
549+
550+ """
551+ def uuid6_to_uuid1 ( uuid6 ) do
552+ { format , ub6 } = uuid_string_to_hex_pair ( uuid6 )
553+
554+ << time_hi :: 12 , time_mid :: 16 , time_low1 :: 20 , @ uuid_v6 :: 4 , time_low2 :: 12 ,
555+ rest :: binary >> = ub6
556+ << time_low :: 32 >> = << time_low1 :: 20 , time_low2 :: 12 >>
557+
558+ << time_low :: 32 , time_mid :: 16 , @ uuid_v1 :: 4 , time_hi :: 12 , rest :: binary >>
559+ |> uuid_to_string ( format )
560+ end
561+
453562 #
454563 # Internal utility functions.
455564 #
@@ -584,6 +693,13 @@ defmodule UUID do
584693 << rnd_hi :: 7 , 1 :: 1 , rnd_low :: 40 >>
585694 end
586695
696+ defp uuid6_node ( :mac_address ) do
697+ uuid1_node ( )
698+ end
699+ defp uuid6_node ( :random_bytes ) do
700+ :crypto . strong_rand_bytes ( 6 )
701+ end
702+
587703 # Generate a hash of the given data.
588704 defp namebased_uuid ( :md5 , data ) do
589705 md5 = :crypto . hash ( :md5 , data )
0 commit comments