1 package org
.argeo
.api
.uuid
;
3 import java
.security
.MessageDigest
;
4 import java
.security
.NoSuchAlgorithmException
;
5 import java
.time
.Duration
;
6 import java
.time
.temporal
.Temporal
;
7 import java
.util
.BitSet
;
8 import java
.util
.Objects
;
9 import java
.util
.Random
;
10 import java
.util
.UUID
;
13 * Implementation of the basic RFC4122 algorithms.
15 * @see "https://datatracker.ietf.org/doc/html/rfc4122"
17 public abstract class AbstractUuidFactory
implements UuidFactory
{
20 * TIME-BASED (version 1)
23 private final static long MOST_SIG_VERSION1
= (1l << 12);
24 private final static long LEAST_SIG_RFC4122_VARIANT
= (1l << 63);
27 protected UUID
createTimeUUID(long timestamp
, long clockSequence
, byte[] node
, int offset
) {
28 Objects
.requireNonNull(node
, "Node array cannot be null");
29 if (node
.length
< offset
+ 6)
30 throw new IllegalArgumentException("Node array must be at least 6 bytes long");
32 long mostSig
= MOST_SIG_VERSION1
// base for version 1 UUID
33 | ((timestamp
& 0xFFFFFFFFL
) << 32) // time_low
34 | (((timestamp
>> 32) & 0xFFFFL
) << 16) // time_mid
35 | ((timestamp
>> 48) & 0x0FFFL
);// time_hi_and_version
37 long leastSig
= LEAST_SIG_RFC4122_VARIANT
// base for Leach–Salz UUID
38 | (((clockSequence
& 0x3F00) >> 8) << 56) // clk_seq_hi_res
39 | ((clockSequence
& 0xFF) << 48) // clk_seq_low
40 | (node
[offset
] & 0xFFL
) //
41 | ((node
[offset
+ 1] & 0xFFL
) << 8) //
42 | ((node
[offset
+ 2] & 0xFFL
) << 16) //
43 | ((node
[offset
+ 3] & 0xFFL
) << 24) //
44 | ((node
[offset
+ 4] & 0xFFL
) << 32) //
45 | ((node
[offset
+ 5] & 0xFFL
) << 40); //
46 UUID uuid
= new UUID(mostSig
, leastSig
);
49 assert uuid
.version() == 1;
50 assert uuid
.variant() == 2;
51 assert uuid
.node() == BitSet
.valueOf(node
).toLongArray()[0];
52 assert uuid
.timestamp() == timestamp
;
53 assert uuid
.clockSequence() == clockSequence
;
58 protected UUID
timeUUID(Temporal time
, long clockSequence
, byte[] node
, int offset
) {
60 Duration duration
= Duration
.between(TimeUuid
.TIMESTAMP_ZERO
, time
);
61 // Number of 100 ns intervals in one second: 1000000000 / 100 = 10000000
62 long timestamp
= duration
.getSeconds() * 10000000 + duration
.getNano() / 100;
63 return createTimeUUID(timestamp
, clockSequence
, node
, offset
);
67 * NAME BASED (version 3 and 5)
69 protected UUID
createNameUUIDv5(UUID namespace
, byte[] name
) {
70 return createNameUUIDv5Static(namespace
, name
);
73 static UUID
createNameUUIDv5Static(UUID namespace
, byte[] name
) {
74 Objects
.requireNonNull(namespace
, "Namespace cannot be null");
75 Objects
.requireNonNull(name
, "Name cannot be null");
77 byte[] bytes
= sha1(UuidBinaryUtils
.toBytes(namespace
), name
);
79 bytes
[6] |= 0x50;// v5
81 bytes
[8] |= 0x80;// variant 1
82 UUID result
= UuidBinaryUtils
.fromBytes(bytes
, 0);
86 protected UUID
createNameUUIDv3(UUID namespace
, byte[] name
) {
87 return createNameUUIDv3Static(namespace
, name
);
90 static UUID
createNameUUIDv3Static(UUID namespace
, byte[] name
) {
91 Objects
.requireNonNull(namespace
, "Namespace cannot be null");
92 Objects
.requireNonNull(name
, "Name cannot be null");
94 byte[] arr
= new byte[name
.length
+ 16];
95 UuidBinaryUtils
.copyBytes(namespace
, arr
, 0);
96 System
.arraycopy(name
, 0, arr
, 16, name
.length
);
97 return UUID
.nameUUIDFromBytes(arr
);
103 protected UUID
createRandomUUID(Random random
) {
104 byte[] arr
= new byte[16];
105 random
.nextBytes(arr
);
109 arr
[8] |= 0x80;// variant 1
110 return UuidBinaryUtils
.fromBytes(arr
);
117 private final static String MD5
= "MD5";
118 private final static String SHA1
= "SHA1";
120 protected static byte[] sha1(byte[]... bytes
) {
121 MessageDigest digest
= getSha1Digest();
122 for (byte[] arr
: bytes
)
124 byte[] checksum
= digest
.digest();
128 protected static byte[] md5(byte[]... bytes
) {
129 MessageDigest digest
= getMd5Digest();
130 for (byte[] arr
: bytes
)
132 byte[] checksum
= digest
.digest();
136 protected static MessageDigest
getSha1Digest() {
137 return getDigest(SHA1
);
140 protected static MessageDigest
getMd5Digest() {
141 return getDigest(MD5
);
144 private static MessageDigest
getDigest(String name
) {
146 return MessageDigest
.getInstance(name
);
147 } catch (NoSuchAlgorithmException e
) {
148 throw new UnsupportedOperationException(name
+ " digest is not avalaible", e
);